|
MythTV
0.26-pre
|
00001 00002 // Own header 00003 #include "mythsystem.h" 00004 #include "system-unix.h" 00005 #include "mythmiscutil.h" 00006 00007 // compat header 00008 #include "compat.h" 00009 00010 // C++/C headers 00011 #include <cerrno> 00012 #include <unistd.h> 00013 #include <stdlib.h> 00014 #include <fcntl.h> 00015 #include <time.h> 00016 #include <signal.h> // for kill() 00017 #include <string.h> 00018 #include <sys/select.h> 00019 #include <sys/wait.h> 00020 #include <iostream> 00021 00022 // QT headers 00023 #include <QCoreApplication> 00024 #include <QMutex> 00025 #include <QMap> 00026 #include <QString> 00027 #include <QStringList> 00028 00029 // libmythbase headers 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 * MythSystemManager method defines 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; // 10ms 00108 nanosleep(&ts, NULL); // ~100x per second, for ~3MBps throughput 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 // Get the corresponding MythSystem instance, and the stdout/stderr 00172 // type 00173 fdLock.lock(); 00174 FDType_t *fdType = fdMap.value(fd); 00175 fdLock.unlock(); 00176 00177 // Emit the data ready signal (1 = stdout, 2 = stderr) 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 // build descriptor list 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 // run_system is set to NULL during shutdown, and we need this thread to 00266 // exit during shutdown. 00267 while( run_system ) 00268 { 00269 struct timespec ts; 00270 ts.tv_sec = 0; 00271 ts.tv_nsec = 100 * 1000 * 1000; // 100ms 00272 nanosleep(&ts, NULL); // sleep 100ms 00273 00274 // check for any running processes 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 // check for any newly exited processes 00289 listLock.lock(); 00290 while( (pid = waitpid(-1, &status, WNOHANG)) > 0 ) 00291 { 00292 m_mapLock.lock(); 00293 // unmanaged process has exited 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 // pop exited process off managed list, add to cleanup list 00303 ms = m_pMap.take(pid); 00304 m_mapLock.unlock(); 00305 00306 // Occasionally, the caller has deleted the structure from under 00307 // our feet. If so, just log and move on. 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 // Deal with (primarily) Ubuntu which seems to consistently be 00319 // screwing up and reporting the signalled case as an exit. This 00320 // workaround will limit the valid exit value to 0 - 127. As all 00321 // of our return values match that (other than the occasional 255) 00322 // this shouldn't cause any issues 00323 if (ms->m_parent->onlyLowExitVal() && WIFEXITED(status) && 00324 WEXITSTATUS(status) != 255 && WEXITSTATUS(status) & 0x80) 00325 { 00326 // Just byte swap the status and it seems to be correctly done 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 // handle normal exit 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 // handle forced exit 00346 else if( WIFSIGNALED(status) ) 00347 { 00348 // Don't override a timed out process which gets killed, but 00349 // otherwise set the return status appropriately 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 // handle abnormal exit (crash) 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 // loop through running processes for any that require action 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 // handle processes beyond marked timeout 00389 if( ms->m_timeout > 0 && ms->m_timeout < now ) 00390 { 00391 // issuing KILL signal after TERM failed in a timely manner 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 // Prevent constant attempts to kill an obstinate child 00398 ms->m_timeout = 0; 00399 ms->Signal(SIGKILL); 00400 } 00401 00402 // issuing TERM signal 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 // hold off unlocking until all the way down here to 00424 // give the buffer handling a chance to run before 00425 // being closed down by signal thread 00426 listLock.unlock(); 00427 } 00428 00429 // kick to allow them to close themselves cleanly 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 // spawn separate thread for signals to prevent manager 00476 // thread from blocking in some slot 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; // 50ms 00491 nanosleep(&ts, NULL); // sleep 50ms 00492 00493 while( run_system ) 00494 { 00495 // handle cleanup and signalling for closed processes 00496 listLock.lock(); 00497 if( msList.isEmpty() ) 00498 { 00499 listLock.unlock(); 00500 break; 00501 } 00502 MythSystemUnix *ms = msList.takeFirst(); 00503 listLock.unlock(); 00504 00505 // This can happen if it has been deleted already 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 * MythSystem method defines 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 // Start the threads if they haven't been started yet. 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 // QBuffers may also need freeing 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 // pass through escape (\), quote ("), and any whitespace 00608 tmp += *i; 00609 else if (whitechr.contains(*i)) 00610 // process whitespace escape code, and pass character 00611 tmp += whitespace[whitechr.indexOf(*i)+1]; 00612 else 00613 // unhandled escape code, abort 00614 return false; 00615 00616 escaped = false; 00617 } 00618 00619 else if (*i == escape) 00620 { 00621 if (hardquoted) 00622 // hard quotes (') pass everything 00623 tmp += *i; 00624 else 00625 // otherwise, mark escaped to handle next character 00626 escaped = true; 00627 } 00628 00629 else if ((quoted & (*i == quote)) || 00630 (hardquoted && (*i == hardquote))) 00631 // end of quoted sequence 00632 quoted = hardquoted = false; 00633 00634 else 00635 // pass through character 00636 tmp += *i; 00637 } 00638 00639 else if (escaped) 00640 { 00641 if ((*i == quote) || (*i == hardquote) || (*i == escape) || 00642 whitespace.contains(*i)) 00643 // pass through special characters 00644 tmp += *i; 00645 else if (whitechr.contains(*i)) 00646 // process whitespace escape code, and pass character 00647 tmp += whitespace[whitechr.indexOf(*i)+1]; 00648 else 00649 // unhandled escape code, abort 00650 return false; 00651 00652 escaped = false; 00653 } 00654 00655 // handle quotes and escape characters 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 // handle whitespace characters 00664 else if (whitespace.contains(*i) && !tmp.isEmpty()) 00665 { 00666 args << tmp; 00667 tmp.clear(); 00668 } 00669 00670 else 00671 // pass everything else 00672 tmp += *i; 00673 00674 // step forward to next character 00675 ++i; 00676 } 00677 00678 if (quoted || hardquoted || escaped) 00679 // command not terminated cleanly 00680 return false; 00681 00682 if (!tmp.isEmpty()) 00683 // collect last argument 00684 args << tmp; 00685 00686 if (args.isEmpty()) 00687 // this shouldnt happen 00688 return false; 00689 00690 // grab the first argument to use as the command 00691 abscmd = args.takeFirst(); 00692 if (!abscmd.startsWith('/')) 00693 { 00694 // search for absolute path 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 // send KILL if it does not exit within one second 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 // For use in the child 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 /* set up pipes */ 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 // set up command args 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 // check before fork to avoid QString use in child 00831 bool setpgidsetting = GetSetting("SetPGID"); 00832 00833 int niceval = m_parent->GetNice(); 00834 int ioprioval = m_parent->GetIOPrio(); 00835 00836 /* Do this before forking in case the child miserably fails */ 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 /* Fork failed, still in parent */ 00846 LOG(VB_SYSTEM, LOG_ERR, "fork() failed: " + ENO); 00847 SetStatus( GENERIC_EXIT_NOT_OK ); 00848 } 00849 else if( child > 0 ) 00850 { 00851 /* parent */ 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 /* close unused pipe ends */ 00863 CLOSE(p_stdin[0]); 00864 CLOSE(p_stdout[1]); 00865 CLOSE(p_stderr[1]); 00866 00867 // store the rest 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 /* Child - NOTE: it is not safe to use LOG or QString between the 00875 * fork and execv calls in the child. It causes occasional locking 00876 * issues that cause deadlocked child processes. */ 00877 00878 /* handle standard input */ 00879 if( p_stdin[0] >= 0 ) 00880 { 00881 /* try to attach stdin to input pipe - failure is fatal */ 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 /* try to attach stdin to /dev/null */ 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 /* handle standard output */ 00914 if( p_stdout[1] >= 0 ) 00915 { 00916 /* try to attach stdout to output pipe - failure is fatal */ 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 /* We aren't sucking this down, redirect stdout to /dev/null */ 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 /* handle standard err */ 00949 if( p_stderr[1] >= 0 ) 00950 { 00951 /* try to attach stderr to error pipe - failure is fatal */ 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 /* We aren't sucking this down, redirect stderr to /dev/null */ 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 /* Close all open file descriptors except stdin/stdout/stderr */ 00984 for( int i = sysconf(_SC_OPEN_MAX) - 1; i > 2; i-- ) 00985 close(i); 00986 00987 /* set directory */ 00988 if( directory && chdir(directory) < 0 ) 00989 { 00990 cerr << locerr 00991 << "chdir() failed: " 00992 << strerror(errno) << endl; 00993 } 00994 00995 /* Set the process group id to be the same as the pid of this child 00996 * process. This ensures that any subprocesses launched by this 00997 * process can be killed along with the process itself. */ 00998 if (setpgidsetting && setpgid(0,0) < 0 ) 00999 { 01000 cerr << locerr 01001 << "setpgid() failed: " 01002 << strerror(errno) << endl; 01003 } 01004 01005 /* Set nice and ioprio values if non-default */ 01006 if (niceval) 01007 myth_nice(niceval); 01008 if (ioprioval) 01009 myth_ioprio(ioprioval); 01010 01011 /* run command */ 01012 if( execv(command, cmdargs) < 0 ) 01013 { 01014 // Can't use LOG due to locking fun. 01015 cerr << locerr 01016 << "execv() failed: " 01017 << strerror(errno) << endl; 01018 } 01019 01020 /* Failed to exec */ 01021 _exit(GENERIC_EXIT_DAEMONIZING_ERROR); // this exit is ok 01022 } 01023 01024 /* Parent */ 01025 01026 // clean up the memory use 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 * vim:ts=4:sw=4:ai:et:si:sts=4 01073 */
1.7.6.1