00001
00002
00003 #include "mythsystem.h"
00004 #include "system-unix.h"
00005 #include "mythmiscutil.h"
00006
00007
00008 #include "compat.h"
00009
00010
00011 #include <cerrno>
00012 #include <unistd.h>
00013 #include <stdlib.h>
00014 #include <fcntl.h>
00015 #include <time.h>
00016 #include <signal.h>
00017 #include <string.h>
00018 #include <sys/select.h>
00019 #include <sys/wait.h>
00020 #include <iostream>
00021
00022
00023 #include <QCoreApplication>
00024 #include <QMutex>
00025 #include <QMap>
00026 #include <QString>
00027 #include <QStringList>
00028
00029
00030 #include "mythcorecontext.h"
00031 #include "mythevent.h"
00032 #include "exitcodes.h"
00033 #include "mythlogging.h"
00034
00035 #define CLOSE(x) \
00036 if( (x) >= 0 ) { \
00037 close((x)); \
00038 fdLock.lock(); \
00039 delete fdMap.value((x)); \
00040 fdMap.remove((x)); \
00041 fdLock.unlock(); \
00042 (x) = -1; \
00043 }
00044
00045 typedef struct
00046 {
00047 MythSystemUnix *ms;
00048 int type;
00049 } FDType_t;
00050 typedef QMap<int, FDType_t*> FDMap_t;
00051
00052
00053
00054
00055 static bool run_system = true;
00056 static MythSystemManager *manager = NULL;
00057 static MythSystemSignalManager *smanager = NULL;
00058 static MythSystemIOHandler *readThread = NULL;
00059 static MythSystemIOHandler *writeThread = NULL;
00060 static MSList_t msList;
00061 static QMutex listLock;
00062 static FDMap_t fdMap;
00063 static QMutex fdLock;
00064
00065 void ShutdownMythSystem(void)
00066 {
00067 run_system = false;
00068 if (manager)
00069 manager->wait();
00070 if (smanager)
00071 smanager->wait();
00072 if (readThread)
00073 readThread->wait();
00074 if (writeThread)
00075 writeThread->wait();
00076 }
00077
00078 MythSystemIOHandler::MythSystemIOHandler(bool read) :
00079 MThread(QString("SystemIOHandler%1").arg(read ? "R" : "W")),
00080 m_pWaitLock(), m_pWait(), m_pLock(), m_pMap(PMap_t()), m_maxfd(-1),
00081 m_read(read)
00082 {
00083 m_readbuf[0] = '\0';
00084 }
00085
00086 void MythSystemIOHandler::run(void)
00087 {
00088 RunProlog();
00089 LOG(VB_GENERAL, LOG_INFO, QString("Starting IO manager (%1)")
00090 .arg(m_read ? "read" : "write"));
00091
00092 m_pLock.lock();
00093 BuildFDs();
00094 m_pLock.unlock();
00095
00096 while( run_system )
00097 {
00098 {
00099 QMutexLocker locker(&m_pWaitLock);
00100 m_pWait.wait(&m_pWaitLock);
00101 }
00102
00103 while( run_system )
00104 {
00105 struct timespec ts;
00106 ts.tv_sec = 0;
00107 ts.tv_nsec = 10*1000*1000;
00108 nanosleep(&ts, NULL);
00109 m_pLock.lock();
00110 if( m_pMap.isEmpty() )
00111 {
00112 m_pLock.unlock();
00113 break;
00114 }
00115
00116 timeval tv;
00117 tv.tv_sec = 0; tv.tv_usec = 0;
00118
00119 int retval;
00120 fd_set fds = m_fds;
00121
00122 if( m_read )
00123 retval = select(m_maxfd+1, &fds, NULL, NULL, &tv);
00124 else
00125 retval = select(m_maxfd+1, NULL, &fds, NULL, &tv);
00126
00127 if( retval == -1 )
00128 LOG(VB_SYSTEM, LOG_ERR,
00129 QString("MythSystemIOHandler: select(%1, %2) failed: %3")
00130 .arg(m_maxfd+1).arg(m_read).arg(strerror(errno)));
00131
00132 else if( retval > 0 )
00133 {
00134 PMap_t::iterator i, next;
00135 for( i = m_pMap.begin(); i != m_pMap.end(); i = next )
00136 {
00137 next = i+1;
00138 int fd = i.key();
00139 if( FD_ISSET(fd, &fds) )
00140 {
00141 if( m_read )
00142 HandleRead(i.key(), i.value());
00143 else
00144 HandleWrite(i.key(), i.value());
00145 }
00146 }
00147 }
00148 m_pLock.unlock();
00149 }
00150 }
00151
00152 RunEpilog();
00153 }
00154
00155 void MythSystemIOHandler::HandleRead(int fd, QBuffer *buff)
00156 {
00157 int len;
00158 errno = 0;
00159 if( (len = read(fd, &m_readbuf, 65536)) <= 0 )
00160 {
00161 if( errno != EAGAIN )
00162 {
00163 m_pMap.remove(fd);
00164 BuildFDs();
00165 }
00166 }
00167 else
00168 {
00169 buff->buffer().append(m_readbuf, len);
00170
00171
00172
00173 fdLock.lock();
00174 FDType_t *fdType = fdMap.value(fd);
00175 fdLock.unlock();
00176
00177
00178 MythSystemUnix *ms = fdType->ms;
00179 emit ms->readDataReady(fdType->type);
00180 }
00181 }
00182
00183 void MythSystemIOHandler::HandleWrite(int fd, QBuffer *buff)
00184 {
00185 if( buff->atEnd() )
00186 {
00187 m_pMap.remove(fd);
00188 BuildFDs();
00189 return;
00190 }
00191
00192 int pos = buff->pos();
00193 int len = buff->size() - pos;
00194 len = (len > 32768 ? 32768 : len);
00195
00196 int rlen = write(fd, buff->read(len).constData(), len);
00197 if( rlen < 0 )
00198 {
00199 if( errno != EAGAIN )
00200 {
00201 m_pMap.remove(fd);
00202 BuildFDs();
00203 }
00204 else
00205 buff->seek(pos);
00206 }
00207 else if( rlen != len )
00208 buff->seek(pos+rlen);
00209 }
00210
00211 void MythSystemIOHandler::insert(int fd, QBuffer *buff)
00212 {
00213 m_pLock.lock();
00214 m_pMap.insert(fd, buff);
00215 BuildFDs();
00216 m_pLock.unlock();
00217 wake();
00218 }
00219
00220 void MythSystemIOHandler::remove(int fd)
00221 {
00222 m_pLock.lock();
00223 if (m_read)
00224 {
00225 PMap_t::iterator i;
00226 i = m_pMap.find(fd);
00227 if ( i != m_pMap.end() )
00228 HandleRead(i.key(), i.value());
00229 }
00230 m_pMap.remove(fd);
00231 BuildFDs();
00232 m_pLock.unlock();
00233 }
00234
00235 void MythSystemIOHandler::wake()
00236 {
00237 QMutexLocker locker(&m_pWaitLock);
00238 m_pWait.wakeAll();
00239 }
00240
00241 void MythSystemIOHandler::BuildFDs()
00242 {
00243
00244 FD_ZERO(&m_fds);
00245 m_maxfd = -1;
00246
00247 PMap_t::iterator i;
00248 for( i = m_pMap.begin(); i != m_pMap.end(); ++i )
00249 {
00250 FD_SET(i.key(), &m_fds);
00251 m_maxfd = (i.key() > m_maxfd ? i.key() : m_maxfd);
00252 }
00253 }
00254
00255 MythSystemManager::MythSystemManager() : MThread("SystemManager")
00256 {
00257 m_jumpAbort = false;
00258 }
00259
00260 void MythSystemManager::run(void)
00261 {
00262 RunProlog();
00263 LOG(VB_GENERAL, LOG_INFO, "Starting process manager");
00264
00265
00266
00267 while( run_system )
00268 {
00269 struct timespec ts;
00270 ts.tv_sec = 0;
00271 ts.tv_nsec = 100 * 1000 * 1000;
00272 nanosleep(&ts, NULL);
00273
00274
00275 m_mapLock.lock();
00276
00277 if( m_pMap.isEmpty() )
00278 {
00279 m_mapLock.unlock();
00280 continue;
00281 }
00282 m_mapLock.unlock();
00283
00284 MythSystemUnix *ms;
00285 pid_t pid;
00286 int status;
00287
00288
00289 listLock.lock();
00290 while( (pid = waitpid(-1, &status, WNOHANG)) > 0 )
00291 {
00292 m_mapLock.lock();
00293
00294 if( !m_pMap.contains(pid) )
00295 {
00296 LOG(VB_SYSTEM, LOG_INFO,
00297 QString("Unmanaged child (PID: %1) has exited!") .arg(pid));
00298 m_mapLock.unlock();
00299 continue;
00300 }
00301
00302
00303 ms = m_pMap.take(pid);
00304 m_mapLock.unlock();
00305
00306
00307
00308 if (!ms)
00309 {
00310 LOG(VB_SYSTEM, LOG_ERR,
00311 QString("Structure for child PID %1 already deleted!")
00312 .arg(pid));
00313 continue;
00314 }
00315
00316 msList.append(ms);
00317
00318
00319
00320
00321
00322
00323 if (ms->m_parent->onlyLowExitVal() && WIFEXITED(status) &&
00324 WEXITSTATUS(status) != 255 && WEXITSTATUS(status) & 0x80)
00325 {
00326
00327 uint16_t oldstatus = status;
00328 status = ((status & 0x00FF) << 8) | ((status & 0xFF00) >> 8);
00329 LOG(VB_SYSTEM, LOG_INFO,
00330 QString("Odd return value: swapping from %1 to %2")
00331 .arg(oldstatus) .arg(status));
00332 }
00333
00334
00335 if( WIFEXITED(status) )
00336 {
00337 ms->SetStatus(WEXITSTATUS(status));
00338 LOG(VB_SYSTEM, LOG_INFO,
00339 QString("Managed child (PID: %1) has exited! "
00340 "command=%2, status=%3, result=%4")
00341 .arg(pid) .arg(ms->GetLogCmd()) .arg(status)
00342 .arg(ms->GetStatus()));
00343 }
00344
00345
00346 else if( WIFSIGNALED(status) )
00347 {
00348
00349
00350 if (ms->GetStatus() != GENERIC_EXIT_TIMEOUT)
00351 ms->SetStatus( GENERIC_EXIT_KILLED );
00352
00353 int sig = WTERMSIG(status);
00354 LOG(VB_SYSTEM, LOG_INFO,
00355 QString("Managed child (PID: %1) has signalled! "
00356 "command=%2, status=%3, result=%4, signal=%5")
00357 .arg(pid) .arg(ms->GetLogCmd()) .arg(status)
00358 .arg(ms->GetStatus()) .arg(sig));
00359 }
00360
00361
00362 else
00363 {
00364 ms->SetStatus( GENERIC_EXIT_NOT_OK );
00365 LOG(VB_SYSTEM, LOG_ERR,
00366 QString("Managed child (PID: %1) has terminated! "
00367 "command=%2, status=%3, result=%4")
00368 .arg(pid) .arg(ms->GetLogCmd()) .arg(status)
00369 .arg(ms->GetStatus()));
00370 }
00371 }
00372
00373
00374
00375 MSMap_t::iterator i, next;
00376 time_t now = time(NULL);
00377
00378 m_mapLock.lock();
00379 m_jumpLock.lock();
00380 for( i = m_pMap.begin(); i != m_pMap.end(); i = next )
00381 {
00382 next = i + 1;
00383 pid = i.key();
00384 ms = i.value();
00385 if (!ms)
00386 continue;
00387
00388
00389 if( ms->m_timeout > 0 && ms->m_timeout < now )
00390 {
00391
00392 if( ms->GetStatus() == GENERIC_EXIT_TIMEOUT )
00393 {
00394 LOG(VB_SYSTEM, LOG_INFO,
00395 QString("Managed child (PID: %1) timed out"
00396 ", issuing KILL signal").arg(pid));
00397
00398 ms->m_timeout = 0;
00399 ms->Signal(SIGKILL);
00400 }
00401
00402
00403 else
00404 {
00405 LOG(VB_SYSTEM, LOG_INFO,
00406 QString("Managed child (PID: %1) timed out"
00407 ", issuing TERM signal").arg(pid));
00408 ms->SetStatus( GENERIC_EXIT_TIMEOUT );
00409 ms->m_timeout = now + 1;
00410 ms->Term();
00411 }
00412 }
00413
00414 if ( m_jumpAbort && ms->GetSetting("AbortOnJump") )
00415 ms->Term();
00416 }
00417
00418 m_jumpAbort = false;
00419 m_jumpLock.unlock();
00420
00421 m_mapLock.unlock();
00422
00423
00424
00425
00426 listLock.unlock();
00427 }
00428
00429
00430 if (readThread)
00431 readThread->wake();
00432 if (writeThread)
00433 writeThread->wake();
00434
00435 RunEpilog();
00436 }
00437
00438 void MythSystemManager::append(MythSystemUnix *ms)
00439 {
00440 m_mapLock.lock();
00441 m_pMap.insert(ms->m_pid, ms);
00442 m_mapLock.unlock();
00443
00444 fdLock.lock();
00445 if( ms->GetSetting("UseStdin") )
00446 writeThread->insert(ms->m_stdpipe[0], ms->GetBuffer(0));
00447
00448 if( ms->GetSetting("UseStdout") )
00449 {
00450 FDType_t *fdType = new FDType_t;
00451 fdType->ms = ms;
00452 fdType->type = 1;
00453 fdMap.insert( ms->m_stdpipe[1], fdType );
00454 readThread->insert(ms->m_stdpipe[1], ms->GetBuffer(1));
00455 }
00456
00457 if( ms->GetSetting("UseStderr") )
00458 {
00459 FDType_t *fdType = new FDType_t;
00460 fdType->ms = ms;
00461 fdType->type = 2;
00462 fdMap.insert( ms->m_stdpipe[2], fdType );
00463 readThread->insert(ms->m_stdpipe[2], ms->GetBuffer(2));
00464 }
00465 fdLock.unlock();
00466 }
00467
00468 void MythSystemManager::jumpAbort(void)
00469 {
00470 m_jumpLock.lock();
00471 m_jumpAbort = true;
00472 m_jumpLock.unlock();
00473 }
00474
00475
00476
00477 MythSystemSignalManager::MythSystemSignalManager() :
00478 MThread("SystemSignalManager")
00479 {
00480 }
00481
00482 void MythSystemSignalManager::run(void)
00483 {
00484 RunProlog();
00485 LOG(VB_GENERAL, LOG_INFO, "Starting process signal handler");
00486 while( run_system )
00487 {
00488 struct timespec ts;
00489 ts.tv_sec = 0;
00490 ts.tv_nsec = 50 * 1000 * 1000;
00491 nanosleep(&ts, NULL);
00492
00493 while( run_system )
00494 {
00495
00496 listLock.lock();
00497 if( msList.isEmpty() )
00498 {
00499 listLock.unlock();
00500 break;
00501 }
00502 MythSystemUnix *ms = msList.takeFirst();
00503 listLock.unlock();
00504
00505
00506 if (!ms)
00507 continue;
00508
00509 ms->m_parent->HandlePostRun();
00510
00511 if (ms->m_stdpipe[0] > 0)
00512 writeThread->remove(ms->m_stdpipe[0]);
00513 CLOSE(ms->m_stdpipe[0]);
00514
00515 if (ms->m_stdpipe[1] > 0)
00516 readThread->remove(ms->m_stdpipe[1]);
00517 CLOSE(ms->m_stdpipe[1]);
00518
00519 if (ms->m_stdpipe[2] > 0)
00520 readThread->remove(ms->m_stdpipe[2]);
00521 CLOSE(ms->m_stdpipe[2]);
00522
00523 if( ms->GetStatus() == GENERIC_EXIT_OK )
00524 emit ms->finished();
00525 else
00526 emit ms->error(ms->GetStatus());
00527
00528 ms->disconnect();
00529
00530 bool cleanup = ms->m_parent->doAutoCleanup();
00531
00532 ms->Unlock();
00533
00534 if( cleanup )
00535 ms->deleteLater();
00536 }
00537 }
00538 RunEpilog();
00539 }
00540
00541
00542
00543
00544
00545 MythSystemUnix::MythSystemUnix(MythSystem *parent)
00546 {
00547 m_parent = parent;
00548
00549 connect( this, SIGNAL(started()), m_parent, SIGNAL(started()) );
00550 connect( this, SIGNAL(finished()), m_parent, SIGNAL(finished()) );
00551 connect( this, SIGNAL(error(uint)), m_parent, SIGNAL(error(uint)) );
00552 connect( this, SIGNAL(readDataReady(int)), m_parent, SIGNAL(readDataReady(int)) );
00553
00554
00555 if( manager == NULL )
00556 {
00557 manager = new MythSystemManager;
00558 manager->start();
00559 }
00560
00561 if( smanager == NULL )
00562 {
00563 smanager = new MythSystemSignalManager;
00564 smanager->start();
00565 }
00566
00567 if( readThread == NULL )
00568 {
00569 readThread = new MythSystemIOHandler(true);
00570 readThread->start();
00571 }
00572
00573 if( writeThread == NULL )
00574 {
00575 writeThread = new MythSystemIOHandler(false);
00576 writeThread->start();
00577 }
00578 }
00579
00580
00581 MythSystemUnix::~MythSystemUnix(void)
00582 {
00583 }
00584
00585 bool MythSystemUnix::ParseShell(const QString cmd, QString &abscmd,
00586 QStringList &args)
00587 {
00588 QList<QChar> whitespace; whitespace << ' ' << '\t' << '\n' << '\r';
00589 QList<QChar> whitechr; whitechr << 't' << 'n' << 'r';
00590 QChar quote = '"',
00591 hardquote = '\'',
00592 escape = '\\';
00593 bool quoted = false,
00594 hardquoted = false,
00595 escaped = false;
00596
00597 QString tmp;
00598 QString::const_iterator i = cmd.begin();
00599 while (i != cmd.end())
00600 {
00601 if (quoted || hardquoted)
00602 {
00603 if (escaped)
00604 {
00605 if ((quote == *i) || (escape == *i) ||
00606 whitespace.contains(*i))
00607
00608 tmp += *i;
00609 else if (whitechr.contains(*i))
00610
00611 tmp += whitespace[whitechr.indexOf(*i)+1];
00612 else
00613
00614 return false;
00615
00616 escaped = false;
00617 }
00618
00619 else if (*i == escape)
00620 {
00621 if (hardquoted)
00622
00623 tmp += *i;
00624 else
00625
00626 escaped = true;
00627 }
00628
00629 else if ((quoted & (*i == quote)) ||
00630 (hardquoted && (*i == hardquote)))
00631
00632 quoted = hardquoted = false;
00633
00634 else
00635
00636 tmp += *i;
00637 }
00638
00639 else if (escaped)
00640 {
00641 if ((*i == quote) || (*i == hardquote) || (*i == escape) ||
00642 whitespace.contains(*i))
00643
00644 tmp += *i;
00645 else if (whitechr.contains(*i))
00646
00647 tmp += whitespace[whitechr.indexOf(*i)+1];
00648 else
00649
00650 return false;
00651
00652 escaped = false;
00653 }
00654
00655
00656 else if (quote == *i)
00657 quoted = true;
00658 else if (hardquote == *i)
00659 hardquoted = true;
00660 else if (escape == *i)
00661 escaped = true;
00662
00663
00664 else if (whitespace.contains(*i) && !tmp.isEmpty())
00665 {
00666 args << tmp;
00667 tmp.clear();
00668 }
00669
00670 else
00671
00672 tmp += *i;
00673
00674
00675 ++i;
00676 }
00677
00678 if (quoted || hardquoted || escaped)
00679
00680 return false;
00681
00682 if (!tmp.isEmpty())
00683
00684 args << tmp;
00685
00686 if (args.isEmpty())
00687
00688 return false;
00689
00690
00691 abscmd = args.takeFirst();
00692 if (!abscmd.startsWith('/'))
00693 {
00694
00695 QStringList path = QString(getenv("PATH")).split(':');
00696 QStringList::const_iterator i = path.begin();
00697 for (; i != path.end(); ++i)
00698 {
00699 QFile file(QString("%1/%2").arg(*i).arg(abscmd));
00700 if (file.exists())
00701 {
00702 abscmd = file.fileName();
00703 break;
00704 }
00705 }
00706 }
00707
00708 return true;
00709 }
00710
00711 void MythSystemUnix::Term(bool force)
00712 {
00713 int status = GetStatus();
00714 if( (status != GENERIC_EXIT_RUNNING && status != GENERIC_EXIT_TIMEOUT) ||
00715 (m_pid <= 0) )
00716 {
00717 LOG(VB_GENERAL, LOG_DEBUG, QString("Terminate skipped. Status: %1")
00718 .arg(status));
00719 return;
00720 }
00721
00722 Signal(SIGTERM);
00723 if( force )
00724 {
00725
00726 if( m_parent->Wait(1) == GENERIC_EXIT_RUNNING )
00727 Signal(SIGKILL);
00728 }
00729 }
00730
00731 void MythSystemUnix::Signal( int sig )
00732 {
00733 int status = GetStatus();
00734 if( (status != GENERIC_EXIT_RUNNING && status != GENERIC_EXIT_TIMEOUT) ||
00735 (m_pid <= 0) )
00736 {
00737 LOG(VB_GENERAL, LOG_DEBUG, QString("Signal skipped. Status: %1")
00738 .arg(status));
00739 return;
00740 }
00741
00742 LOG(VB_GENERAL, LOG_INFO, QString("Child PID %1 killed with %2")
00743 .arg(m_pid).arg(strsignal(sig)));
00744 kill((GetSetting("SetPGID") ? -m_pid : m_pid), sig);
00745 }
00746
00747 #define MAX_BUFLEN 1024
00748 void MythSystemUnix::Fork(time_t timeout)
00749 {
00750 QString LOC_ERR = QString("myth_system('%1'): Error: ").arg(GetLogCmd());
00751
00752
00753 char locerr[MAX_BUFLEN];
00754 strncpy(locerr, (const char *)LOC_ERR.toUtf8().constData(), MAX_BUFLEN);
00755 locerr[MAX_BUFLEN-1] = '\0';
00756
00757 LOG(VB_SYSTEM, LOG_DEBUG, QString("Launching: %1").arg(GetLogCmd()));
00758
00759 GetBuffer(0)->setBuffer(0);
00760 GetBuffer(1)->setBuffer(0);
00761 GetBuffer(2)->setBuffer(0);
00762
00763 int p_stdin[] = {-1,-1};
00764 int p_stdout[] = {-1,-1};
00765 int p_stderr[] = {-1,-1};
00766
00767
00768 if( GetSetting("UseStdin") )
00769 {
00770 if( pipe(p_stdin) == -1 )
00771 {
00772 LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdin pipe() failed");
00773 SetStatus( GENERIC_EXIT_NOT_OK );
00774 }
00775 else
00776 fcntl(p_stdin[1], F_SETFL, O_NONBLOCK);
00777 }
00778 if( GetSetting("UseStdout") )
00779 {
00780 if( pipe(p_stdout) == -1 )
00781 {
00782 LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdout pipe() failed");
00783 SetStatus( GENERIC_EXIT_NOT_OK );
00784 }
00785 else
00786 fcntl(p_stdout[0], F_SETFL, O_NONBLOCK);
00787 }
00788 if( GetSetting("UseStderr") )
00789 {
00790 if( pipe(p_stderr) == -1 )
00791 {
00792 LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stderr pipe() failed");
00793 SetStatus( GENERIC_EXIT_NOT_OK );
00794 }
00795 else
00796 fcntl(p_stderr[0], F_SETFL, O_NONBLOCK);
00797 }
00798
00799
00800 if (GetSetting("UseShell"))
00801 {
00802 QStringList args = QStringList("-c");
00803 args << GetCommand() + " " + GetArgs().join(" ");
00804 SetArgs( args );
00805 QString cmd = "/bin/sh";
00806 SetCommand( cmd );
00807 }
00808 QStringList args = GetArgs();
00809 args.prepend(GetCommand().split('/').last());
00810 SetArgs( args );
00811
00812 QByteArray cmdUTF8 = GetCommand().toUtf8();
00813 const char *command = strdup(cmdUTF8.constData());
00814
00815 char **cmdargs = (char **)malloc((args.size() + 1) * sizeof(char *));
00816 int i;
00817 QStringList::const_iterator it;
00818
00819 for (i = 0, it = args.constBegin(); it != args.constEnd(); ++it)
00820 {
00821 cmdargs[i++] = strdup(it->toUtf8().constData());
00822 }
00823 cmdargs[i] = NULL;
00824
00825 const char *directory = NULL;
00826 QString dir = GetDirectory();
00827 if (GetSetting("SetDirectory") && !dir.isEmpty())
00828 directory = strdup(dir.toUtf8().constData());
00829
00830
00831 bool setpgidsetting = GetSetting("SetPGID");
00832
00833 int niceval = m_parent->GetNice();
00834 int ioprioval = m_parent->GetIOPrio();
00835
00836
00837 m_timeout = timeout;
00838 if( timeout )
00839 m_timeout += time(NULL);
00840
00841 pid_t child = fork();
00842
00843 if (child < 0)
00844 {
00845
00846 LOG(VB_SYSTEM, LOG_ERR, "fork() failed: " + ENO);
00847 SetStatus( GENERIC_EXIT_NOT_OK );
00848 }
00849 else if( child > 0 )
00850 {
00851
00852 m_pid = child;
00853 SetStatus( GENERIC_EXIT_RUNNING );
00854
00855 LOG(VB_SYSTEM, LOG_INFO,
00856 QString("Managed child (PID: %1) has started! "
00857 "%2%3 command=%4, timeout=%5")
00858 .arg(m_pid) .arg(GetSetting("UseShell") ? "*" : "")
00859 .arg(GetSetting("RunInBackground") ? "&" : "")
00860 .arg(GetLogCmd()) .arg(timeout));
00861
00862
00863 CLOSE(p_stdin[0]);
00864 CLOSE(p_stdout[1]);
00865 CLOSE(p_stderr[1]);
00866
00867
00868 m_stdpipe[0] = p_stdin[1];
00869 m_stdpipe[1] = p_stdout[0];
00870 m_stdpipe[2] = p_stderr[0];
00871 }
00872 else if (child == 0)
00873 {
00874
00875
00876
00877
00878
00879 if( p_stdin[0] >= 0 )
00880 {
00881
00882 if( dup2(p_stdin[0], 0) < 0 )
00883 {
00884 cerr << locerr
00885 << "Cannot redirect input pipe to standard input: "
00886 << strerror(errno) << endl;
00887 _exit(GENERIC_EXIT_PIPE_FAILURE);
00888 }
00889 }
00890 else
00891 {
00892
00893 int fd = open("/dev/null", O_RDONLY);
00894 if( fd >= 0 )
00895 {
00896 if( dup2(fd, 0) < 0)
00897 {
00898 cerr << locerr
00899 << "Cannot redirect /dev/null to standard input,"
00900 "\n\t\t\tfailed to duplicate file descriptor: "
00901 << strerror(errno) << endl;
00902 }
00903 }
00904 else
00905 {
00906 cerr << locerr
00907 << "Cannot redirect /dev/null to standard input, "
00908 "failed to open: "
00909 << strerror(errno) << endl;
00910 }
00911 }
00912
00913
00914 if( p_stdout[1] >= 0 )
00915 {
00916
00917 if( dup2(p_stdout[1], 1) < 0)
00918 {
00919 cerr << locerr
00920 << "Cannot redirect output pipe to standard output: "
00921 << strerror(errno) << endl;
00922 _exit(GENERIC_EXIT_PIPE_FAILURE);
00923 }
00924 }
00925 else
00926 {
00927
00928 int fd = open("/dev/null", O_WRONLY);
00929 if( fd >= 0 )
00930 {
00931 if( dup2(fd, 1) < 0)
00932 {
00933 cerr << locerr
00934 << "Cannot redirect standard output to /dev/null,"
00935 "\n\t\t\tfailed to duplicate file descriptor: "
00936 << strerror(errno) << endl;
00937 }
00938 }
00939 else
00940 {
00941 cerr << locerr
00942 << "Cannot redirect standard output to /dev/null, "
00943 "failed to open: "
00944 << strerror(errno) << endl;
00945 }
00946 }
00947
00948
00949 if( p_stderr[1] >= 0 )
00950 {
00951
00952 if( dup2(p_stderr[1], 2) < 0)
00953 {
00954 cerr << locerr
00955 << "Cannot redirect error pipe to standard error: "
00956 << strerror(errno) << endl;
00957 _exit(GENERIC_EXIT_PIPE_FAILURE);
00958 }
00959 }
00960 else
00961 {
00962
00963 int fd = open("/dev/null", O_WRONLY);
00964 if( fd >= 0 )
00965 {
00966 if( dup2(fd, 2) < 0)
00967 {
00968 cerr << locerr
00969 << "Cannot redirect standard error to /dev/null,"
00970 "\n\t\t\tfailed to duplicate file descriptor: "
00971 << strerror(errno) << endl;
00972 }
00973 }
00974 else
00975 {
00976 cerr << locerr
00977 << "Cannot redirect standard error to /dev/null, "
00978 "failed to open: "
00979 << strerror(errno) << endl;
00980 }
00981 }
00982
00983
00984 for( int i = sysconf(_SC_OPEN_MAX) - 1; i > 2; i-- )
00985 close(i);
00986
00987
00988 if( directory && chdir(directory) < 0 )
00989 {
00990 cerr << locerr
00991 << "chdir() failed: "
00992 << strerror(errno) << endl;
00993 }
00994
00995
00996
00997
00998 if (setpgidsetting && setpgid(0,0) < 0 )
00999 {
01000 cerr << locerr
01001 << "setpgid() failed: "
01002 << strerror(errno) << endl;
01003 }
01004
01005
01006 if (niceval)
01007 myth_nice(niceval);
01008 if (ioprioval)
01009 myth_ioprio(ioprioval);
01010
01011
01012 if( execv(command, cmdargs) < 0 )
01013 {
01014
01015 cerr << locerr
01016 << "execv() failed: "
01017 << strerror(errno) << endl;
01018 }
01019
01020
01021 _exit(GENERIC_EXIT_DAEMONIZING_ERROR);
01022 }
01023
01024
01025
01026
01027 if( command )
01028 free((void *)command);
01029
01030 if( directory )
01031 free((void *)directory);
01032
01033 if( cmdargs )
01034 {
01035 for (i = 0; cmdargs[i]; i++)
01036 free( cmdargs[i] );
01037 free( cmdargs );
01038 }
01039
01040 if( GetStatus() != GENERIC_EXIT_RUNNING )
01041 {
01042 CLOSE(p_stdin[0]);
01043 CLOSE(p_stdin[1]);
01044 CLOSE(p_stdout[0]);
01045 CLOSE(p_stdout[1]);
01046 CLOSE(p_stderr[0]);
01047 CLOSE(p_stderr[1]);
01048 }
01049 }
01050
01051 void MythSystemUnix::Manage(void)
01052 {
01053 if( manager == NULL )
01054 {
01055 manager = new MythSystemManager;
01056 manager->start();
01057 }
01058 manager->append(this);
01059 }
01060
01061 void MythSystemUnix::JumpAbort(void)
01062 {
01063 if( manager == NULL )
01064 {
01065 manager = new MythSystemManager;
01066 manager->start();
01067 }
01068 manager->jumpAbort();
01069 }
01070
01071
01072
01073