00001
00002 #include <cstdlib>
00003
00004
00005 #include <unistd.h>
00006
00007
00008 #include <QCoreApplication>
00009 #include <QKeyEvent>
00010 #include <QEvent>
00011
00012
00013 #include "exitcodes.h"
00014 #include "mythcontext.h"
00015 #include "mythdbcon.h"
00016 #include "lcddevice.h"
00017 #include "tv.h"
00018 #include "compat.h"
00019 #include "mythdirs.h"
00020 #include "remoteutil.h"
00021 #include "mythsystem.h"
00022
00023 #include "welcomedialog.h"
00024 #include "welcomesettings.h"
00025
00026 #define UPDATE_STATUS_INTERVAL 30000
00027 #define UPDATE_SCREEN_INTERVAL 15000
00028
00029
00030 WelcomeDialog::WelcomeDialog(MythScreenStack *parent, const char *name)
00031 :MythScreenType(parent, name),
00032 m_status_text(NULL), m_recording_text(NULL), m_scheduled_text(NULL),
00033 m_warning_text(NULL), m_startfrontend_button(NULL),
00034 m_menuPopup(NULL), m_updateStatusTimer(new QTimer(this)),
00035 m_updateScreenTimer(new QTimer(this)), m_isRecording(false),
00036 m_hasConflicts(false), m_bWillShutdown(false),
00037 m_secondsToShutdown(-1), m_preRollSeconds(0), m_idleWaitForRecordingTime(0),
00038 m_idleTimeoutSecs(0), m_screenTunerNo(0), m_screenScheduledNo(0),
00039 m_statusListNo(0), m_frontendIsRunning(false),
00040 m_pendingRecListUpdate(false), m_pendingSchedUpdate(false)
00041 {
00042 gCoreContext->addListener(this);
00043
00044 m_installDir = GetInstallPrefix();
00045 m_preRollSeconds = gCoreContext->GetNumSetting("RecordPreRoll");
00046 m_idleWaitForRecordingTime =
00047 gCoreContext->GetNumSetting("idleWaitForRecordingTime", 15);
00048
00049 m_timeFormat = gCoreContext->GetSetting("TimeFormat", "h:mm AP");
00050 m_dateFormat = gCoreContext->GetSetting("MythWelcomeDateFormat", "dddd\\ndd MMM yyyy");
00051 m_dateFormat.replace("\\n", "\n");
00052
00053
00054 m_bWillShutdown = (gCoreContext->GetNumSetting("idleTimeoutSecs", 0) != 0);
00055
00056 m_idleTimeoutSecs = gCoreContext->GetNumSetting("idleTimeoutSecs", 0);
00057
00058 connect(m_updateStatusTimer, SIGNAL(timeout()),
00059 this, SLOT(updateStatus()));
00060 m_updateStatusTimer->start(UPDATE_STATUS_INTERVAL);
00061
00062 connect(m_updateScreenTimer, SIGNAL(timeout()),
00063 this, SLOT(updateScreen()));
00064 }
00065
00066 bool WelcomeDialog::Create(void)
00067 {
00068 bool foundtheme = false;
00069
00070
00071 foundtheme = LoadWindowFromXML("welcome-ui.xml", "welcome_screen", this);
00072
00073 if (!foundtheme)
00074 return false;
00075
00076 bool err = false;
00077 UIUtilE::Assign(this, m_status_text, "status_text", &err);
00078 UIUtilE::Assign(this, m_recording_text, "recording_text", &err);
00079 UIUtilE::Assign(this, m_scheduled_text, "scheduled_text", &err);
00080 UIUtilE::Assign(this, m_warning_text, "conflicts_text", &err);
00081 UIUtilE::Assign(this, m_startfrontend_button, "startfrontend_button", &err);
00082
00083 if (err)
00084 {
00085 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'welcome_screen'");
00086 return false;
00087 }
00088
00089 m_warning_text->SetVisible(false);
00090
00091 m_startfrontend_button->SetText(tr("Start Frontend"));
00092 connect(m_startfrontend_button, SIGNAL(Clicked()),
00093 this, SLOT(startFrontendClick()));
00094
00095 BuildFocusList();
00096
00097 SetFocusWidget(m_startfrontend_button);
00098
00099 checkConnectionToServer();
00100 checkAutoStart();
00101
00102 return true;
00103 }
00104
00105 void WelcomeDialog::startFrontend(void)
00106 {
00107 QString startFECmd = gCoreContext->GetSetting("MythWelcomeStartFECmd",
00108 m_installDir + "/bin/mythfrontend");
00109
00110 myth_system(startFECmd);
00111 updateAll();
00112 m_frontendIsRunning = false;
00113 }
00114
00115 void WelcomeDialog::startFrontendClick(void)
00116 {
00117 if (m_frontendIsRunning)
00118 return;
00119
00120 m_frontendIsRunning = true;
00121
00122
00123 QTimer::singleShot(500, this, SLOT(startFrontend()));
00124 }
00125
00126 void WelcomeDialog::checkAutoStart(void)
00127 {
00128
00129
00130 QString command = m_installDir + "/bin/mythshutdown --startup";
00131 command += logPropagateArgs;
00132 uint state = myth_system(command);
00133
00134 LOG(VB_GENERAL, LOG_NOTICE,
00135 QString("mythshutdown --startup returned: %1").arg(state));
00136
00137 bool bAutoStartFrontend = gCoreContext->GetNumSetting("AutoStartFrontend", 1);
00138
00139 if (state == 1 && bAutoStartFrontend)
00140 startFrontendClick();
00141
00142
00143 updateAll();
00144 }
00145
00146 void WelcomeDialog::customEvent(QEvent *e)
00147 {
00148 if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage)
00149 {
00150 MythEvent *me = (MythEvent *) e;
00151
00152 if (me->Message().left(21) == "RECORDING_LIST_CHANGE" ||
00153 me->Message() == "UPDATE_PROG_INFO")
00154 {
00155 LOG(VB_GENERAL, LOG_NOTICE,
00156 "MythWelcome received a recording list change event");
00157
00158 QMutexLocker lock(&m_RecListUpdateMuxtex);
00159
00160 if (pendingRecListUpdate())
00161 {
00162 LOG(VB_GENERAL, LOG_NOTICE,
00163 " [deferred to pending handler]");
00164 }
00165 else
00166 {
00167
00168 QTimer::singleShot(500, this, SLOT(updateRecordingList()));
00169 setPendingRecListUpdate(true);
00170 }
00171 }
00172 else if (me->Message().left(15) == "SCHEDULE_CHANGE")
00173 {
00174 LOG(VB_GENERAL, LOG_NOTICE,
00175 "MythWelcome received a SCHEDULE_CHANGE event");
00176
00177 QMutexLocker lock(&m_SchedUpdateMuxtex);
00178
00179 if (pendingSchedUpdate())
00180 {
00181 LOG(VB_GENERAL, LOG_NOTICE,
00182 " [deferred to pending handler]");
00183 }
00184 else
00185 {
00186 QTimer::singleShot(500, this, SLOT(updateScheduledList()));
00187 setPendingSchedUpdate(true);
00188 }
00189 }
00190 else if (me->Message().left(18) == "SHUTDOWN_COUNTDOWN")
00191 {
00192 #if 0
00193 LOG(VB_GENERAL, LOG_NOTICE,
00194 "MythWelcome received a SHUTDOWN_COUNTDOWN event");
00195 #endif
00196 QString secs = me->Message().mid(19);
00197 m_secondsToShutdown = secs.toInt();
00198 updateStatusMessage();
00199 updateScreen();
00200 }
00201 else if (me->Message().left(12) == "SHUTDOWN_NOW")
00202 {
00203 LOG(VB_GENERAL, LOG_NOTICE,
00204 "MythWelcome received a SHUTDOWN_NOW event");
00205 if (gCoreContext->IsFrontendOnly())
00206 {
00207
00208
00209 if (gCoreContext->GetNumSetting("ShutdownWithMasterBE", 0) == 1)
00210 {
00211 LOG(VB_GENERAL, LOG_NOTICE,
00212 "MythWelcome is shutting this computer down now");
00213 QString poweroff_cmd = gCoreContext->GetSetting("MythShutdownPowerOff", "");
00214 if (!poweroff_cmd.isEmpty())
00215 myth_system(poweroff_cmd);
00216 }
00217 }
00218 }
00219 }
00220 }
00221
00222 bool WelcomeDialog::keyPressEvent(QKeyEvent *event)
00223 {
00224 if (GetFocusWidget()->keyPressEvent(event))
00225 return true;
00226
00227 bool handled = false;
00228 QStringList actions;
00229 handled = GetMythMainWindow()->TranslateKeyPress("Welcome", event, actions);
00230
00231 for (int i = 0; i < actions.size() && !handled; i++)
00232 {
00233 QString action = actions[i];
00234 handled = true;
00235
00236 if (action == "ESCAPE")
00237 {
00238 return true;
00239 }
00240 else if (action == "MENU")
00241 {
00242 showMenu();
00243 }
00244 else if (action == "NEXTVIEW")
00245 {
00246 Close();
00247 }
00248 else if (action == "INFO")
00249 {
00250 MythWelcomeSettings settings;
00251 if (kDialogCodeAccepted == settings.exec())
00252 {
00253 gCoreContext->SendMessage("CLEAR_SETTINGS_CACHE");
00254 updateStatus();
00255 updateScreen();
00256
00257 m_dateFormat = gCoreContext->GetSetting("MythWelcomeDateFormat", "dddd\\ndd MMM yyyy");
00258 m_dateFormat.replace("\\n", "\n");
00259 }
00260 }
00261 else if (action == "SHOWSETTINGS")
00262 {
00263 MythShutdownSettings settings;
00264 if (kDialogCodeAccepted == settings.exec())
00265 gCoreContext->SendMessage("CLEAR_SETTINGS_CACHE");
00266 }
00267 else if (action == "0")
00268 {
00269 QString mythshutdown_status =
00270 m_installDir + "/bin/mythshutdown --status 0";
00271 QString mythshutdown_unlock =
00272 m_installDir + "/bin/mythshutdown --unlock";
00273 QString mythshutdown_lock =
00274 m_installDir + "/bin/mythshutdown --lock";
00275
00276 uint statusCode;
00277 statusCode = myth_system(mythshutdown_status + logPropagateArgs);
00278
00279
00280 if (!(statusCode & 0xFF00) && statusCode & 16)
00281 {
00282 myth_system(mythshutdown_unlock + logPropagateArgs);
00283 }
00284 else
00285 {
00286 myth_system(mythshutdown_lock + logPropagateArgs);
00287 }
00288
00289 updateStatusMessage();
00290 updateScreen();
00291 }
00292 else if (action == "STARTXTERM")
00293 {
00294 QString cmd = gCoreContext->GetSetting("MythShutdownXTermCmd", "");
00295 if (!cmd.isEmpty())
00296 myth_system(cmd);
00297 }
00298 else if (action == "STARTSETUP")
00299 {
00300 QString mythtv_setup = m_installDir + "/bin/mythtv-setup";
00301 myth_system(mythtv_setup + logPropagateArgs);
00302 }
00303 else
00304 handled = false;
00305 }
00306
00307 if (!handled && MythScreenType::keyPressEvent(event))
00308 handled = true;
00309
00310 return handled;
00311 }
00312
00313 void WelcomeDialog::closeDialog()
00314 {
00315 Close();
00316 }
00317
00318 WelcomeDialog::~WelcomeDialog()
00319 {
00320 gCoreContext->removeListener(this);
00321
00322 if (m_updateStatusTimer)
00323 m_updateStatusTimer->disconnect();
00324
00325 if (m_updateScreenTimer)
00326 m_updateScreenTimer->disconnect();
00327 }
00328
00329 void WelcomeDialog::updateStatus(void)
00330 {
00331 checkConnectionToServer();
00332
00333 updateStatusMessage();
00334 }
00335
00336 void WelcomeDialog::updateScreen(void)
00337 {
00338 QString status;
00339
00340 if (!gCoreContext->IsConnectedToMaster())
00341 {
00342 m_recording_text->SetText(tr("Cannot connect to server!"));
00343 m_scheduled_text->SetText(tr("Cannot connect to server!"));
00344 m_warning_text->SetVisible(false);
00345 }
00346 else
00347 {
00348
00349 if (m_isRecording && !m_tunerList.empty())
00350 {
00351 if (m_screenTunerNo >= m_tunerList.size())
00352 m_screenTunerNo = 0;
00353
00354 TunerStatus tuner = m_tunerList[m_screenTunerNo];
00355
00356 if (tuner.isRecording)
00357 {
00358 status = QObject::tr("Tuner %1 is recording:\n")
00359 .arg(tuner.id);
00360 status += tuner.channame;
00361 status += "\n" + tuner.title;
00362 if (!tuner.subtitle.isEmpty())
00363 status += "\n("+tuner.subtitle+")";
00364 status += "\n" + tuner.startTime.toString(m_timeFormat) +
00365 " " + tr("to") + " " + tuner.endTime.toString(m_timeFormat);
00366 }
00367 else
00368 {
00369 status = QObject::tr("Tuner %1 is not recording")
00370 .arg(tuner.id);
00371 }
00372
00373 if (m_screenTunerNo < m_tunerList.size() - 1)
00374 m_screenTunerNo++;
00375 else
00376 m_screenTunerNo = 0;
00377 }
00378 else
00379 status = tr("There are no recordings currently taking place");
00380
00381 status.detach();
00382
00383 m_recording_text->SetText(status);
00384
00385
00386 if (!m_scheduledList.empty())
00387 {
00388 if (m_screenScheduledNo >= m_scheduledList.size())
00389 m_screenScheduledNo = 0;
00390
00391 ProgramInfo progInfo = m_scheduledList[m_screenScheduledNo];
00392
00393 InfoMap infomap;
00394 progInfo.ToMap(infomap);
00395
00396
00397
00398 status = infomap["channame"] + "\n";
00399 status += infomap["title"];
00400 if (!infomap["subtitle"].isEmpty())
00401 status += "\n(" + infomap["subtitle"] + ")";
00402
00403 status += "\n" + infomap["timedate"];
00404
00405 if (m_screenScheduledNo < m_scheduledList.size() - 1)
00406 m_screenScheduledNo++;
00407 else
00408 m_screenScheduledNo = 0;
00409 }
00410 else
00411 status = tr("There are no scheduled recordings");
00412
00413 m_scheduled_text->SetText(status);
00414 }
00415
00416
00417 if (m_statusList.empty())
00418 status = tr("Please Wait...");
00419 else
00420 {
00421 if ((int)m_statusListNo >= m_statusList.count())
00422 m_statusListNo = 0;
00423
00424 status = m_statusList[m_statusListNo];
00425 if (m_statusList.count() > 1)
00426 status += "...";
00427 m_status_text->SetText(status);
00428
00429 if ((int)m_statusListNo < m_statusList.count() - 1)
00430 m_statusListNo++;
00431 else
00432 m_statusListNo = 0;
00433 }
00434
00435 m_updateScreenTimer->stop();
00436 m_updateScreenTimer->setSingleShot(true);
00437 m_updateScreenTimer->start(UPDATE_SCREEN_INTERVAL);
00438 }
00439
00440
00441 void WelcomeDialog::runMythFillDatabase()
00442 {
00443 QString command;
00444
00445 QString mfpath = gCoreContext->GetSetting("MythFillDatabasePath",
00446 "mythfilldatabase");
00447 QString mfarg = gCoreContext->GetSetting("MythFillDatabaseArgs", "");
00448
00449 command = QString("%1 %2").arg(mfpath).arg(mfarg);
00450 command += logPropagateArgs;
00451
00452 command += "&";
00453
00454 LOG(VB_GENERAL, LOG_INFO, QString("Grabbing EPG data using command: %1\n")
00455 .arg(command));
00456
00457 myth_system(command);
00458 }
00459
00460 void WelcomeDialog::updateAll(void)
00461 {
00462 updateRecordingList();
00463 updateScheduledList();
00464 }
00465
00466 bool WelcomeDialog::updateRecordingList()
00467 {
00468 {
00469
00470
00471 QMutexLocker lock(&m_RecListUpdateMuxtex);
00472 setPendingRecListUpdate(false);
00473 }
00474
00475 m_tunerList.clear();
00476 m_isRecording = false;
00477 m_screenTunerNo = 0;
00478
00479 if (!gCoreContext->IsConnectedToMaster())
00480 return false;
00481
00482 m_isRecording = RemoteGetRecordingStatus(&m_tunerList, true);
00483
00484 return true;
00485 }
00486
00487 bool WelcomeDialog::updateScheduledList()
00488 {
00489 {
00490
00491
00492 QMutexLocker lock(&m_SchedUpdateMuxtex);
00493 setPendingSchedUpdate(false);
00494 }
00495
00496 m_scheduledList.clear();
00497 m_screenScheduledNo = 0;
00498
00499 if (!gCoreContext->IsConnectedToMaster())
00500 {
00501 updateStatusMessage();
00502 return false;
00503 }
00504
00505 GetNextRecordingList(m_nextRecordingStart, &m_hasConflicts,
00506 &m_scheduledList);
00507
00508 updateStatus();
00509 updateScreen();
00510
00511 return true;
00512 }
00513
00514 void WelcomeDialog::updateStatusMessage(void)
00515 {
00516 m_statusList.clear();
00517
00518 QDateTime curtime = QDateTime::currentDateTime();
00519
00520 if (!m_isRecording && !m_nextRecordingStart.isNull() &&
00521 curtime.secsTo(m_nextRecordingStart) - m_preRollSeconds <
00522 (m_idleWaitForRecordingTime * 60) + m_idleTimeoutSecs)
00523 {
00524 m_statusList.append(tr("MythTV is about to start recording."));
00525 }
00526
00527 if (m_isRecording)
00528 {
00529 m_statusList.append(tr("MythTV is busy recording."));
00530 }
00531
00532 QString mythshutdown_status = m_installDir + "/bin/mythshutdown --status 0";
00533 uint statusCode = myth_system(mythshutdown_status + logPropagateArgs);
00534
00535 if (!(statusCode & 0xFF00))
00536 {
00537 if (statusCode & 1)
00538 m_statusList.append(tr("MythTV is busy transcoding."));
00539 if (statusCode & 2)
00540 m_statusList.append(tr("MythTV is busy flagging commercials."));
00541 if (statusCode & 4)
00542 m_statusList.append(tr("MythTV is busy grabbing EPG data."));
00543 if (statusCode & 16)
00544 m_statusList.append(tr("MythTV is locked by a user."));
00545 if (statusCode & 32)
00546 m_statusList.append(tr("MythTV has running or pending jobs."));
00547 if (statusCode & 64)
00548 m_statusList.append(tr("MythTV is in a daily wakeup/shutdown period."));
00549 if (statusCode & 128)
00550 m_statusList.append(tr("MythTV is about to start a wakeup/shutdown period."));
00551 }
00552
00553 if (m_statusList.empty())
00554 {
00555 if (m_bWillShutdown && m_secondsToShutdown != -1)
00556 m_statusList.append(tr("MythTV is idle and will shutdown in %n "
00557 "second(s).", "", m_secondsToShutdown));
00558 else
00559 m_statusList.append(tr("MythTV is idle."));
00560 }
00561
00562 m_warning_text->SetVisible(m_hasConflicts);
00563 }
00564
00565 bool WelcomeDialog::checkConnectionToServer(void)
00566 {
00567 m_updateStatusTimer->stop();
00568
00569 bool bRes = false;
00570
00571 if (gCoreContext->IsConnectedToMaster())
00572 bRes = true;
00573 else
00574 {
00575 if (gCoreContext->ConnectToMasterServer(false))
00576 {
00577 bRes = true;
00578 updateAll();
00579 }
00580 else
00581 updateScreen();
00582 }
00583
00584 if (bRes)
00585 m_updateStatusTimer->start(UPDATE_STATUS_INTERVAL);
00586 else
00587 m_updateStatusTimer->start(5000);
00588
00589 return bRes;
00590 }
00591
00592 void WelcomeDialog::showMenu(void)
00593 {
00594 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00595
00596 m_menuPopup = new MythDialogBox("Menu", popupStack, "actionmenu");
00597
00598 if (m_menuPopup->Create())
00599 popupStack->AddScreen(m_menuPopup);
00600
00601 m_menuPopup->SetReturnEvent(this, "action");
00602
00603 QString mythshutdown_status = m_installDir + "/bin/mythshutdown --status 0";
00604 uint statusCode = myth_system(mythshutdown_status + logPropagateArgs);
00605
00606 if (!(statusCode & 0xFF00) && statusCode & 16)
00607 m_menuPopup->AddButton(tr("Unlock Shutdown"), SLOT(unlockShutdown()));
00608 else
00609 m_menuPopup->AddButton(tr("Lock Shutdown"), SLOT(lockShutdown()));
00610
00611 m_menuPopup->AddButton(tr("Run mythfilldatabase"), SLOT(runEPGGrabber()));
00612 m_menuPopup->AddButton(tr("Shutdown Now"), SLOT(shutdownNow()));
00613 m_menuPopup->AddButton(tr("Exit"), SLOT(closeDialog()));
00614 m_menuPopup->AddButton(tr("Cancel"));
00615 }
00616
00617 void WelcomeDialog::lockShutdown(void)
00618 {
00619 QString command = m_installDir + "/bin/mythshutdown --lock";
00620 command += logPropagateArgs;
00621 myth_system(command);
00622 updateStatusMessage();
00623 updateScreen();
00624 }
00625
00626 void WelcomeDialog::unlockShutdown(void)
00627 {
00628 QString command = m_installDir + "/bin/mythshutdown --unlock";
00629 command += logPropagateArgs;
00630 myth_system(command);
00631 updateStatusMessage();
00632 updateScreen();
00633 }
00634
00635 void WelcomeDialog::runEPGGrabber(void)
00636 {
00637 runMythFillDatabase();
00638 sleep(1);
00639 updateStatusMessage();
00640 updateScreen();
00641 }
00642
00643 void WelcomeDialog::shutdownNow(void)
00644 {
00645
00646 if (gCoreContext->IsFrontendOnly())
00647 {
00648 LOG(VB_GENERAL, LOG_INFO,
00649 "MythWelcome is shutting this computer down now");
00650 QString poweroff_cmd = gCoreContext->GetSetting("MythShutdownPowerOff", "");
00651 if (!poweroff_cmd.isEmpty())
00652 myth_system(poweroff_cmd);
00653 return;
00654 }
00655
00656
00657 if (m_isRecording)
00658 {
00659 ShowOkPopup(tr("Cannot shutdown because MythTV is currently recording"));
00660 return;
00661 }
00662
00663 QDateTime curtime = QDateTime::currentDateTime();
00664
00665
00666 if (!m_nextRecordingStart.isNull() &&
00667 curtime.secsTo(m_nextRecordingStart) - m_preRollSeconds <
00668 (m_idleWaitForRecordingTime * 60) + m_idleTimeoutSecs)
00669 {
00670 ShowOkPopup(tr("Cannot shutdown because MythTV is about to start recording"));
00671 return;
00672 }
00673
00674
00675 QString command = m_installDir + "/bin/mythshutdown --status 0";
00676 command += logPropagateArgs;
00677
00678 uint statusCode = myth_system(command);
00679 if (!(statusCode & 0xFF00) && statusCode & 128)
00680 {
00681 ShowOkPopup(tr("Cannot shutdown because MythTV is about to start "
00682 "a wakeup/shutdown period."));
00683 return;
00684 }
00685
00686
00687 if (!m_nextRecordingStart.isNull())
00688 {
00689 QDateTime restarttime = m_nextRecordingStart.addSecs((-1) * m_preRollSeconds);
00690
00691 int add = gCoreContext->GetNumSetting("StartupSecsBeforeRecording", 240);
00692 if (add)
00693 restarttime = restarttime.addSecs((-1) * add);
00694
00695 QString wakeup_timeformat = gCoreContext->GetSetting("WakeupTimeFormat",
00696 "yyyy-MM-ddThh:mm");
00697 QString setwakeup_cmd = gCoreContext->GetSetting("SetWakeuptimeCommand",
00698 "echo \'Wakeuptime would "
00699 "be $time if command "
00700 "set.\'");
00701
00702 if (wakeup_timeformat == "time_t")
00703 {
00704 QString time_ts;
00705 setwakeup_cmd.replace("$time",
00706 time_ts.setNum(restarttime.toTime_t()));
00707 }
00708 else
00709 setwakeup_cmd.replace("$time",
00710 restarttime.toString(wakeup_timeformat));
00711
00712 if (!setwakeup_cmd.isEmpty())
00713 {
00714 myth_system(setwakeup_cmd);
00715 }
00716 }
00717
00718
00719 command = "sudo " + m_installDir + "/bin/mythshutdown --shutdown";
00720 command += logPropagateArgs;
00721
00722 myth_system(command);
00723 }
00724