MythTV  0.26-pre
system-unix.cpp
Go to the documentation of this file.
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  */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends