00001
00002
00003 #include "compat.h"
00004
00005
00006 #include "mythsystem.h"
00007
00008
00009 #include <cerrno>
00010 #include <unistd.h>
00011 #include <stdlib.h>
00012 #include <fcntl.h>
00013 #include <time.h>
00014 #include <signal.h>
00015 #include <string.h>
00016
00017
00018 #include <QCoreApplication>
00019
00020
00021 #include "mythcorecontext.h"
00022 #include "mythevent.h"
00023 #include "mythlogging.h"
00024 #include "exitcodes.h"
00025
00026 #if CONFIG_CYGWIN || defined(_WIN32)
00027 #include "system-windows.h"
00028 #else
00029 #include "system-unix.h"
00030 #endif
00031
00032
00033
00034
00035
00036
00037 void MythSystem::initializePrivate(void)
00038 {
00039 m_nice = 0;
00040 m_ioprio = 0;
00041 #if CONFIG_CYGWIN || defined(_WIN32)
00042 d = new MythSystemWindows(this);
00043 #else
00044 d = new MythSystemUnix(this);
00045 #endif
00046 }
00047
00048 MythSystem::MythSystem()
00049 {
00050 m_semReady.release(1);
00051 initializePrivate();
00052 }
00053
00054 MythSystem::MythSystem(const QString &command, uint flags)
00055 {
00056 m_semReady.release(1);
00057 initializePrivate();
00058 SetCommand(command, flags);
00059 }
00060
00064 void MythSystem::SetCommand(const QString &command, uint flags)
00065 {
00066 if (flags & kMSRunShell)
00067 SetCommand(command, QStringList(), flags);
00068 else
00069 {
00070 QString abscommand;
00071 QStringList args;
00072 if (!d->ParseShell(command, abscommand, args))
00073 {
00074 LOG(VB_GENERAL, LOG_ERR,
00075 QString("MythSystem(%1) command not understood")
00076 .arg(command));
00077 m_status = GENERIC_EXIT_INVALID_CMDLINE;
00078 return;
00079 }
00080
00081 SetCommand(abscommand, args, flags);
00082 }
00083 }
00084
00085
00086 MythSystem::MythSystem(const QString &command,
00087 const QStringList &args, uint flags)
00088 {
00089 m_semReady.release(1);
00090 initializePrivate();
00091 SetCommand(command, args, flags);
00092 }
00093
00098 void MythSystem::SetCommand(const QString &command,
00099 const QStringList &args, uint flags)
00100 {
00101 m_status = GENERIC_EXIT_START;
00102 m_command = QString(command).trimmed();
00103 m_args = QStringList(args);
00104
00105 ProcessFlags(flags);
00106
00107
00108 if (!GetSetting("UseShell") && access(command.toUtf8().constData(), X_OK))
00109 {
00110 LOG(VB_GENERAL, LOG_ERR, QString("MythSystem(%1) command not executable, ")
00111 .arg(command) + ENO);
00112 m_status = GENERIC_EXIT_CMD_NOT_FOUND;
00113 }
00114
00115 m_logcmd = (m_command + " " + m_args.join(" ")).trimmed();
00116
00117 if( GetSetting("AnonLog") )
00118 {
00119 m_logcmd.truncate(m_logcmd.indexOf(" "));
00120 m_logcmd.append(" (anonymized)");
00121 }
00122 }
00123
00124
00125 MythSystem::MythSystem(const MythSystem &other) :
00126 d(other.d),
00127 m_status(other.m_status),
00128
00129 m_command(other.m_command),
00130 m_logcmd(other.m_logcmd),
00131 m_args(other.m_args),
00132 m_directory(other.m_directory),
00133
00134 m_settings(other.m_settings)
00135 {
00136 m_semReady.release(other.m_semReady.available());
00137 }
00138
00139
00140 MythSystem::~MythSystem(void)
00141 {
00142 delete d;
00143 }
00144
00145
00146 void MythSystem::SetDirectory(const QString &directory)
00147 {
00148 m_settings["SetDirectory"] = true;
00149 m_directory = QString(directory);
00150 }
00151
00152 bool MythSystem::SetNice(int nice)
00153 {
00154 if( !d || (GetStatus() != GENERIC_EXIT_START) )
00155 return false;
00156
00157 m_nice = nice;
00158 return true;
00159 }
00160
00161 bool MythSystem::SetIOPrio(int prio)
00162 {
00163 if( !d || (GetStatus() != GENERIC_EXIT_START) )
00164 return false;
00165
00166 m_ioprio = prio;
00167 return true;
00168 }
00169
00173 void MythSystem::Run(time_t timeout)
00174 {
00175 if( !d )
00176 m_status = GENERIC_EXIT_NO_HANDLER;
00177
00178 if( GetStatus() != GENERIC_EXIT_START )
00179 {
00180 emit error(GetStatus());
00181 return;
00182 }
00183
00184
00185 HandlePreRun();
00186
00187 d->Fork(timeout);
00188
00189 if( GetStatus() == GENERIC_EXIT_RUNNING )
00190 {
00191 m_semReady.acquire(1);
00192 emit started();
00193 d->Manage();
00194 }
00195 else
00196 {
00197 emit error(GetStatus());
00198 }
00199 }
00200
00201
00202
00203 uint MythSystem::Wait(time_t timeout)
00204 {
00205 if( !d )
00206 m_status = GENERIC_EXIT_NO_HANDLER;
00207
00208 if( (GetStatus() != GENERIC_EXIT_RUNNING) || GetSetting("RunInBackground") )
00209 return GetStatus();
00210
00211 if( GetSetting("ProcessEvents") )
00212 {
00213 if( timeout > 0 )
00214 timeout += time(NULL);
00215
00216 while( !timeout || time(NULL) < timeout )
00217 {
00218
00219 if( m_semReady.tryAcquire(1,100) )
00220 {
00221 m_semReady.release(1);
00222 break;
00223 }
00224
00225 qApp->processEvents();
00226 }
00227 }
00228 else
00229 {
00230 if( timeout > 0 )
00231 {
00232 if( m_semReady.tryAcquire(1, timeout*1000) )
00233 m_semReady.release(1);
00234 }
00235 else
00236 {
00237 m_semReady.acquire(1);
00238 m_semReady.release(1);
00239 }
00240 }
00241 return GetStatus();
00242 }
00243
00244 void MythSystem::Term(bool force)
00245 {
00246 if( !d )
00247 m_status = GENERIC_EXIT_NO_HANDLER;
00248
00249 if( m_status != GENERIC_EXIT_RUNNING )
00250 return;
00251
00252 d->Term(force);
00253 }
00254
00255 void MythSystem::Signal( int sig )
00256 {
00257 if( !d )
00258 m_status = GENERIC_EXIT_NO_HANDLER;
00259
00260 if( m_status != GENERIC_EXIT_RUNNING )
00261 return;
00262
00263 d->Signal(sig);
00264 }
00265
00266
00267 void MythSystem::ProcessFlags(uint flags)
00268 {
00269 if( m_status != GENERIC_EXIT_START )
00270 {
00271 LOG(VB_SYSTEM, LOG_DEBUG, QString("status: %1").arg(m_status));
00272 return;
00273 }
00274
00275 m_settings["IsInUI"] = gCoreContext->HasGUI() && gCoreContext->IsUIThread();
00276
00277 if( flags & kMSRunBackground )
00278 m_settings["RunInBackground"] = true;
00279
00280 if( m_command.endsWith("&") )
00281 {
00282 if (!GetSetting("RunInBackground"))
00283 LOG(VB_SYSTEM, LOG_DEBUG, "Adding background flag");
00284
00285
00286 m_command.chop(1);
00287 m_command = m_command.trimmed();
00288 m_settings["RunInBackground"] = true;
00289 m_settings["UseShell"] = true;
00290 m_settings["IsInUI"] = false;
00291 }
00292
00293 if( GetSetting("IsInUI") )
00294 {
00295
00296 m_settings["BlockInputDevs"] = !(flags & kMSDontBlockInputDevs);
00297 m_settings["DisableDrawing"] = !(flags & kMSDontDisableDrawing);
00298 m_settings["ProcessEvents"] = flags & kMSProcessEvents;
00299 }
00300
00301 if( flags & kMSStdIn )
00302 m_settings["UseStdin"] = true;
00303 if( flags & kMSStdOut )
00304 m_settings["UseStdout"] = true;
00305 if( flags & kMSStdErr )
00306 m_settings["UseStderr"] = true;
00307 if( flags & kMSRunShell )
00308 m_settings["UseShell"] = true;
00309 if( flags & kMSNoRunShell )
00310 m_settings["UseShell"] = false;
00311 if( flags & kMSAbortOnJump )
00312 m_settings["AbortOnJump"] = true;
00313 if( flags & kMSSetPGID )
00314 m_settings["SetPGID"] = true;
00315 if( flags & kMSAutoCleanup && GetSetting("RunInBackground") )
00316 m_settings["AutoCleanup"] = true;
00317 if( flags & kMSAnonLog )
00318 m_settings["AnonLog"] = true;
00319 if( flags & kMSLowExitVal )
00320 m_settings["OnlyLowExitVal"] = true;
00321 }
00322
00323 QByteArray MythSystem::Read(int size)
00324 {
00325 return m_stdbuff[1].read(size);
00326 }
00327
00328 QByteArray MythSystem::ReadErr(int size)
00329 {
00330 return m_stdbuff[2].read(size);
00331 }
00332
00333 QByteArray& MythSystem::ReadAll()
00334 {
00335 return m_stdbuff[1].buffer();
00336 }
00337
00338 QByteArray& MythSystem::ReadAllErr()
00339 {
00340 return m_stdbuff[2].buffer();
00341 }
00342
00343 int MythSystem::Write(const QByteArray &ba)
00344 {
00345 if (!GetSetting("UseStdin"))
00346 return 0;
00347
00348 m_stdbuff[0].buffer().append(ba.constData());
00349 return ba.size();
00350 }
00351
00352 void MythSystem::HandlePreRun()
00353 {
00354
00355
00356
00357 if( GetSetting("BlockInputDevs") )
00358 {
00359 QEvent event(MythEvent::kLockInputDevicesEventType);
00360 QCoreApplication::sendEvent(gCoreContext->GetGUIObject(), &event);
00361 }
00362
00363
00364
00365
00366 if( GetSetting("DisableDrawing") )
00367 {
00368 QEvent event(MythEvent::kPushDisableDrawingEventType);
00369 QCoreApplication::sendEvent(gCoreContext->GetGUIObject(), &event);
00370 }
00371 }
00372
00373 void MythSystem::HandlePostRun()
00374 {
00375
00376
00377 if( GetSetting("DisableDrawing") )
00378 {
00379 QEvent *event = new QEvent(MythEvent::kPopDisableDrawingEventType);
00380 QCoreApplication::postEvent(gCoreContext->GetGUIObject(), event);
00381 }
00382
00383
00384
00385 if( GetSetting("BlockInputDevs") )
00386 {
00387 QEvent *event = new QEvent(MythEvent::kUnlockInputDevicesEventType);
00388 QCoreApplication::postEvent(gCoreContext->GetGUIObject(), event);
00389 }
00390 }
00391
00392 void MythSystem::JumpAbort(void)
00393 {
00394 if (!d)
00395 return;
00396
00397 LOG(VB_SYSTEM, LOG_DEBUG, "Triggering Abort on Jumppoint");
00398 d->JumpAbort();
00399 }
00400
00401 uint myth_system(const QString &command, uint flags, uint timeout)
00402 {
00403 flags |= kMSRunShell | kMSAutoCleanup;
00404 MythSystem *ms = new MythSystem(command, flags);
00405 ms->Run(timeout);
00406 uint result = ms->Wait(0);
00407 if (!ms->GetSetting("RunInBackground"))
00408 ms->deleteLater();
00409
00410 return result;
00411 }
00412
00413 void myth_system_jump_abort(void)
00414 {
00415 MythSystem ms;
00416 ms.JumpAbort();
00417 }
00418
00419 extern "C" {
00420 unsigned int myth_system_c(char *command, uint flags, uint timeout)
00421 {
00422 QString cmd(command);
00423 return myth_system(cmd, flags, timeout);
00424 }
00425 }
00426
00427
00428
00429