|
MythTV
0.26-pre
|
00001 /* -*- Mode: c++ -*- 00002 * 00003 * Class CommandLineArg 00004 * Class MythCommandLineParser 00005 * 00006 * Copyright (C) Raymond Wagner 2011 00007 * 00008 * This program is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2 of the License, or 00011 * (at your option) any later version. 00012 * 00013 * This program is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with this program; if not, write to the Free Software 00020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00021 */ 00022 00023 #include <iostream> 00024 #include <fstream> 00025 #include <stdio.h> 00026 #include <stdlib.h> 00027 #include <algorithm> 00028 #include <sys/types.h> 00029 #include <unistd.h> 00030 00031 #ifndef _WIN32 00032 #include <sys/ioctl.h> 00033 #include <pwd.h> 00034 #include <grp.h> 00035 #if defined(__linux__) || defined(__LINUX__) 00036 #include <sys/prctl.h> 00037 #endif 00038 #endif 00039 00040 using namespace std; 00041 00042 #include <QDir> 00043 #include <QFile> 00044 #include <QFileInfo> 00045 #include <QSize> 00046 #include <QVariant> 00047 #include <QVariantList> 00048 #include <QVariantMap> 00049 #include <QString> 00050 #include <QCoreApplication> 00051 #include <QTextStream> 00052 #include <QDateTime> 00053 00054 #include "mythcommandlineparser.h" 00055 #include "mythcorecontext.h" 00056 #include "exitcodes.h" 00057 #include "mythconfig.h" 00058 #include "mythlogging.h" 00059 #include "mythversion.h" 00060 #include "logging.h" 00061 #include "mythmiscutil.h" 00062 00063 #define TERMWIDTH 79 00064 00065 const int kEnd = 0, 00066 kEmpty = 1, 00067 kOptOnly = 2, 00068 kOptVal = 3, 00069 kArg = 4, 00070 kPassthrough = 5, 00071 kInvalid = 6; 00072 00073 const char* NamedOptType(int type); 00074 bool openPidfile(ofstream &pidfs, const QString &pidfile); 00075 bool setUser(const QString &username); 00076 int GetTermWidth(void); 00077 00081 int GetTermWidth(void) 00082 { 00083 #ifdef _WIN32 00084 return TERMWIDTH; 00085 #else 00086 struct winsize ws; 00087 00088 if (ioctl(0, TIOCGWINSZ, &ws) != 0) 00089 return TERMWIDTH; 00090 00091 return (int)ws.ws_col; 00092 #endif 00093 } 00094 00098 const char* NamedOptType(int type) 00099 { 00100 switch (type) 00101 { 00102 case kEnd: 00103 return "kEnd"; 00104 00105 case kEmpty: 00106 return "kEmpty"; 00107 00108 case kOptOnly: 00109 return "kOptOnly"; 00110 00111 case kOptVal: 00112 return "kOptVal"; 00113 00114 case kArg: 00115 return "kArg"; 00116 00117 case kPassthrough: 00118 return "kPassthrough"; 00119 00120 case kInvalid: 00121 return "kInvalid"; 00122 00123 default: 00124 return "kUnknown"; 00125 } 00126 } 00127 00144 CommandLineArg::CommandLineArg(QString name, QVariant::Type type, 00145 QVariant def, QString help, QString longhelp) : 00146 ReferenceCounter(), m_given(false), m_converted(false), m_name(name), 00147 m_group(""), m_deprecated(""), m_removed(""), m_removedversion(""), 00148 m_type(type), m_default(def), m_help(help), m_longhelp(longhelp) 00149 { 00150 if ((m_type != QVariant::String) && (m_type != QVariant::StringList) && 00151 (m_type != QVariant::Map)) 00152 m_converted = true; 00153 } 00154 00161 CommandLineArg::CommandLineArg(QString name, QVariant::Type type, QVariant def) 00162 : ReferenceCounter(), m_given(false), m_converted(false), m_name(name), 00163 m_group(""), m_deprecated(""), m_removed(""), m_removedversion(""), 00164 m_type(type), m_default(def) 00165 { 00166 if ((m_type != QVariant::String) && (m_type != QVariant::StringList) && 00167 (m_type != QVariant::Map)) 00168 m_converted = true; 00169 } 00170 00178 CommandLineArg::CommandLineArg(QString name) : 00179 ReferenceCounter(), m_given(false), m_converted(false), m_name(name), 00180 m_deprecated(""), m_removed(""), m_removedversion(""), 00181 m_type(QVariant::Invalid) 00182 { 00183 } 00184 00188 QString CommandLineArg::GetKeywordString(void) const 00189 { 00190 // this may cause problems if the terminal is too narrow, or if too 00191 // many keywords for the same argument are used 00192 return m_keywords.join(" OR "); 00193 } 00194 00198 int CommandLineArg::GetKeywordLength(void) const 00199 { 00200 int len = GetKeywordString().length(); 00201 00202 QList<CommandLineArg*>::const_iterator i1; 00203 for (i1 = m_parents.begin(); i1 != m_parents.end(); ++i1) 00204 len = max(len, (*i1)->GetKeywordLength()+2); 00205 00206 return len; 00207 } 00208 00224 QString CommandLineArg::GetHelpString(int off, QString group, bool force) const 00225 { 00226 QString helpstr; 00227 QTextStream msg(&helpstr, QIODevice::WriteOnly); 00228 int termwidth = GetTermWidth(); 00229 00230 if (m_help.isEmpty() && !force) 00231 // only print if there is a short help to print 00232 return helpstr; 00233 00234 if ((m_group != group) && !force) 00235 // only print if looping over the correct group 00236 return helpstr; 00237 00238 if (!m_parents.isEmpty() && !force) 00239 // only print if an independent option, not subject 00240 // to a parent option 00241 return helpstr; 00242 00243 if (!m_deprecated.isEmpty()) 00244 // option is marked as deprecated, do not show 00245 return helpstr; 00246 00247 if (!m_removed.isEmpty()) 00248 // option is marked as removed, do not show 00249 return helpstr; 00250 00251 QString pad; 00252 pad.fill(' ', off); 00253 00254 // print the first line with the available keywords 00255 QStringList hlist = m_help.split('\n'); 00256 wrapList(hlist, termwidth-off); 00257 if (!m_parents.isEmpty()) 00258 msg << " "; 00259 msg << GetKeywordString().leftJustified(off, ' ') 00260 << hlist[0] << endl; 00261 00262 // print remaining lines with necessary padding 00263 QStringList::const_iterator i1; 00264 for (i1 = hlist.begin() + 1; i1 != hlist.end(); ++i1) 00265 msg << pad << *i1 << endl; 00266 00267 // loop through any child arguments to print underneath 00268 QList<CommandLineArg*>::const_iterator i2; 00269 for (i2 = m_children.begin(); i2 != m_children.end(); ++i2) 00270 msg << (*i2)->GetHelpString(off, group, true); 00271 00272 msg.flush(); 00273 return helpstr; 00274 } 00275 00283 QString CommandLineArg::GetLongHelpString(QString keyword) const 00284 { 00285 QString helpstr; 00286 QTextStream msg(&helpstr, QIODevice::WriteOnly); 00287 int termwidth = GetTermWidth(); 00288 00289 // help called for an argument that is not me, this should not happen 00290 if (!m_keywords.contains(keyword)) 00291 return helpstr; 00292 00293 // argument has been marked as removed, so warn user of such 00294 if (!m_removed.isEmpty()) 00295 PrintRemovedWarning(keyword); 00296 // argument has been marked as deprecated, so warn user of such 00297 else if (!m_deprecated.isEmpty()) 00298 PrintDeprecatedWarning(keyword); 00299 00300 msg << "Option: " << keyword << endl << endl; 00301 00302 bool first = true; 00303 00304 // print all related keywords, padding for multiples 00305 QStringList::const_iterator i1; 00306 for (i1 = m_keywords.begin(); i1 != m_keywords.end(); ++i1) 00307 { 00308 if (*i1 != keyword) 00309 { 00310 if (first) 00311 { 00312 msg << "Aliases: " << *i1 << endl; 00313 first = false; 00314 } 00315 else 00316 msg << " " << *i1 << endl; 00317 } 00318 } 00319 00320 // print type and default for the stored value 00321 msg << "Type: " << QVariant::typeToName(m_type) << endl; 00322 if (m_default.canConvert(QVariant::String)) 00323 msg << "Default: " << m_default.toString() << endl; 00324 00325 QStringList help; 00326 if (m_longhelp.isEmpty()) 00327 help = m_help.split("\n"); 00328 else 00329 help = m_longhelp.split("\n"); 00330 wrapList(help, termwidth-13); 00331 00332 // print description, wrapping and padding as necessary 00333 msg << "Description: " << help[0] << endl; 00334 for (i1 = help.begin() + 1; i1 != help.end(); ++i1) 00335 msg << " " << *i1 << endl; 00336 00337 QList<CommandLineArg*>::const_iterator i2; 00338 00339 // loop through the four relation types and print 00340 if (!m_parents.isEmpty()) 00341 { 00342 msg << endl << "Can be used in combination with:" << endl; 00343 for (i2 = m_parents.constBegin(); i2 != m_parents.constEnd(); ++i2) 00344 msg << " " << (*i2)->GetPreferredKeyword() 00345 .toLocal8Bit().constData(); 00346 msg << endl; 00347 } 00348 00349 if (!m_children.isEmpty()) 00350 { 00351 msg << endl << "Allows the use of:" << endl; 00352 for (i2 = m_children.constBegin(); i2 != m_children.constEnd(); ++i2) 00353 msg << " " << (*i2)->GetPreferredKeyword() 00354 .toLocal8Bit().constData(); 00355 msg << endl; 00356 } 00357 00358 if (!m_requires.isEmpty()) 00359 { 00360 msg << endl << "Requires the use of:" << endl; 00361 for (i2 = m_requires.constBegin(); i2 != m_requires.constEnd(); ++i2) 00362 msg << " " << (*i2)->GetPreferredKeyword() 00363 .toLocal8Bit().constData(); 00364 msg << endl; 00365 } 00366 00367 if (!m_blocks.isEmpty()) 00368 { 00369 msg << endl << "Prevents the use of:" << endl; 00370 for (i2 = m_blocks.constBegin(); i2 != m_blocks.constEnd(); ++i2) 00371 msg << " " << (*i2)->GetPreferredKeyword() 00372 .toLocal8Bit().constData(); 00373 msg << endl; 00374 } 00375 00376 msg.flush(); 00377 return helpstr; 00378 } 00379 00386 bool CommandLineArg::Set(QString opt) 00387 { 00388 m_usedKeyword = opt; 00389 00390 switch (m_type) 00391 { 00392 case QVariant::Bool: 00393 m_stored = QVariant(!m_default.toBool()); 00394 break; 00395 00396 case QVariant::Int: 00397 if (m_stored.isNull()) 00398 m_stored = QVariant(1); 00399 else 00400 m_stored = QVariant(m_stored.toInt() + 1); 00401 break; 00402 00403 case QVariant::String: 00404 m_stored = m_default; 00405 break; 00406 00407 default: 00408 cerr << "Command line option did not receive value:" << endl 00409 << " " << opt.toLocal8Bit().constData() << endl; 00410 return false; 00411 } 00412 00413 m_given = true; 00414 return true; 00415 } 00416 00419 bool CommandLineArg::Set(QString opt, QByteArray val) 00420 { 00421 QVariantList vlist; 00422 QList<QByteArray> blist; 00423 QVariantMap vmap; 00424 m_usedKeyword = opt; 00425 00426 switch (m_type) 00427 { 00428 case QVariant::Bool: 00429 cerr << "Boolean type options do not accept values:" << endl 00430 << " " << opt.toLocal8Bit().constData() << endl; 00431 return false; 00432 00433 case QVariant::String: 00434 m_stored = QVariant(val); 00435 break; 00436 00437 case QVariant::Int: 00438 m_stored = QVariant(val.toInt()); 00439 break; 00440 00441 case QVariant::UInt: 00442 m_stored = QVariant(val.toUInt()); 00443 break; 00444 00445 case QVariant::LongLong: 00446 m_stored = QVariant(val.toLongLong()); 00447 break; 00448 00449 case QVariant::Double: 00450 m_stored = QVariant(val.toDouble()); 00451 break; 00452 00453 case QVariant::DateTime: 00454 m_stored = QVariant(myth_dt_from_string(QString(val))); 00455 break; 00456 00457 case QVariant::StringList: 00458 if (!m_stored.isNull()) 00459 vlist = m_stored.toList(); 00460 vlist << val; 00461 m_stored = QVariant(vlist); 00462 break; 00463 00464 case QVariant::Map: 00465 if (!val.contains('=')) 00466 { 00467 cerr << "Command line option did not get expected " 00468 << "key/value pair" << endl; 00469 return false; 00470 } 00471 00472 blist = val.split('='); 00473 00474 if (!m_stored.isNull()) 00475 vmap = m_stored.toMap(); 00476 vmap[QString(blist[0])] = QVariant(blist[1]); 00477 m_stored = QVariant(vmap); 00478 break; 00479 00480 case QVariant::Size: 00481 if (!val.contains('x')) 00482 { 00483 cerr << "Command line option did not get expected " 00484 << "XxY pair" << endl; 00485 return false; 00486 } 00487 00488 blist = val.split('x'); 00489 m_stored = QVariant(QSize(blist[0].toInt(), blist[1].toInt())); 00490 break; 00491 00492 default: 00493 m_stored = QVariant(val); 00494 } 00495 00496 m_given = true; 00497 return true; 00498 } 00499 00502 CommandLineArg* CommandLineArg::SetParentOf(QString opt) 00503 { 00504 m_children << new CommandLineArg(opt); 00505 return this; 00506 } 00507 00510 CommandLineArg* CommandLineArg::SetParentOf(QStringList opts) 00511 { 00512 QStringList::const_iterator i = opts.begin(); 00513 for (; i != opts.end(); ++i) 00514 m_children << new CommandLineArg(*i); 00515 return this; 00516 } 00517 00520 CommandLineArg* CommandLineArg::SetParent(QString opt) 00521 { 00522 m_parents << new CommandLineArg(opt); 00523 return this; 00524 } 00525 00528 CommandLineArg* CommandLineArg::SetParent(QStringList opts) 00529 { 00530 QStringList::const_iterator i = opts.begin(); 00531 for (; i != opts.end(); ++i) 00532 m_parents << new CommandLineArg(*i); 00533 return this; 00534 } 00535 00538 CommandLineArg* CommandLineArg::SetChildOf(QString opt) 00539 { 00540 m_parents << new CommandLineArg(opt); 00541 return this; 00542 } 00543 00546 CommandLineArg* CommandLineArg::SetChildOf(QStringList opts) 00547 { 00548 QStringList::const_iterator i = opts.begin(); 00549 for (; i != opts.end(); ++i) 00550 m_parents << new CommandLineArg(*i); 00551 return this; 00552 } 00553 00556 CommandLineArg* CommandLineArg::SetChild(QString opt) 00557 { 00558 m_children << new CommandLineArg(opt); 00559 return this; 00560 } 00561 00564 CommandLineArg* CommandLineArg::SetChild(QStringList opts) 00565 { 00566 QStringList::const_iterator i = opts.begin(); 00567 for (; i != opts.end(); ++i) 00568 m_children << new CommandLineArg(*i); 00569 return this; 00570 } 00571 00574 CommandLineArg* CommandLineArg::SetRequiredChild(QString opt) 00575 { 00576 m_children << new CommandLineArg(opt); 00577 m_requires << new CommandLineArg(opt); 00578 return this; 00579 } 00580 00583 CommandLineArg* CommandLineArg::SetRequiredChild(QStringList opts) 00584 { 00585 QStringList::const_iterator i = opts.begin(); 00586 for (; i != opts.end(); ++i) 00587 { 00588 m_children << new CommandLineArg(*i); 00589 m_requires << new CommandLineArg(*i); 00590 } 00591 return this; 00592 } 00593 00596 CommandLineArg* CommandLineArg::SetRequiredChildOf(QString opt) 00597 { 00598 m_parents << new CommandLineArg(opt); 00599 m_requiredby << new CommandLineArg(opt); 00600 return this; 00601 } 00602 00605 CommandLineArg* CommandLineArg::SetRequiredChildOf(QStringList opts) 00606 { 00607 QStringList::const_iterator i = opts.begin(); 00608 for (; i != opts.end(); ++i) 00609 { 00610 m_parents << new CommandLineArg(*i); 00611 m_requiredby << new CommandLineArg(*i); 00612 } 00613 return this; 00614 } 00615 00618 CommandLineArg* CommandLineArg::SetRequires(QString opt) 00619 { 00620 m_requires << new CommandLineArg(opt); 00621 return this; 00622 } 00623 00626 CommandLineArg* CommandLineArg::SetRequires(QStringList opts) 00627 { 00628 QStringList::const_iterator i = opts.begin(); 00629 for (; i != opts.end(); ++i) 00630 m_requires << new CommandLineArg(*i); 00631 return this; 00632 } 00633 00636 CommandLineArg* CommandLineArg::SetBlocks(QString opt) 00637 { 00638 m_blocks << new CommandLineArg(opt); 00639 return this; 00640 } 00641 00644 CommandLineArg* CommandLineArg::SetBlocks(QStringList opts) 00645 { 00646 QStringList::const_iterator i = opts.begin(); 00647 for (; i != opts.end(); ++i) 00648 m_blocks << new CommandLineArg(*i); 00649 return this; 00650 } 00651 00654 CommandLineArg* CommandLineArg::SetDeprecated(QString depstr) 00655 { 00656 if (depstr.isEmpty()) 00657 depstr = "and will be removed in a future version."; 00658 m_deprecated = depstr; 00659 return this; 00660 } 00661 00664 CommandLineArg* CommandLineArg::SetRemoved(QString remstr, QString remver) 00665 { 00666 if (remstr.isEmpty()) 00667 remstr = "and is no longer available in this version."; 00668 m_removed = remstr; 00669 m_removedversion = remver; 00670 return this; 00671 } 00672 00678 void CommandLineArg::SetParentOf(CommandLineArg *other, bool forward) 00679 { 00680 int i; 00681 bool replaced = false; 00682 other->UpRef(); 00683 00684 for (i = 0; i < m_children.size(); i++) 00685 { 00686 if (m_children[i]->m_name == other->m_name) 00687 { 00688 m_children[i]->DownRef(); 00689 m_children.replace(i, other); 00690 replaced = true; 00691 break; 00692 } 00693 } 00694 00695 if (!replaced) 00696 m_children << other; 00697 00698 if (forward) 00699 other->SetChildOf(this, false); 00700 } 00701 00707 void CommandLineArg::SetChildOf(CommandLineArg *other, bool forward) 00708 { 00709 int i; 00710 bool replaced = false; 00711 other->UpRef(); 00712 00713 for (i = 0; i < m_parents.size(); i++) 00714 { 00715 if (m_parents[i]->m_name == other->m_name) 00716 { 00717 m_parents[i]->DownRef(); 00718 m_parents.replace(i, other); 00719 replaced = true; 00720 break; 00721 } 00722 } 00723 00724 if (!replaced) 00725 m_parents << other; 00726 00727 if (forward) 00728 other->SetParentOf(this, false); 00729 } 00730 00736 void CommandLineArg::SetRequires(CommandLineArg *other, bool forward) 00737 { 00738 int i; 00739 bool replaced = false; 00740 other->UpRef(); 00741 00742 for (i = 0; i < m_requires.size(); i++) 00743 { 00744 if (m_requires[i]->m_name == other->m_name) 00745 { 00746 m_requires[i]->DownRef(); 00747 m_requires.replace(i, other); 00748 replaced = true; 00749 break; 00750 } 00751 } 00752 00753 if (!replaced) 00754 m_requires << other; 00755 00756 // requirements need not be reciprocal 00757 // if (forward) 00758 // other->SetRequires(this, false); 00759 } 00760 00766 void CommandLineArg::SetBlocks(CommandLineArg *other, bool forward) 00767 { 00768 int i; 00769 bool replaced = false; 00770 other->UpRef(); 00771 00772 for (i = 0; i < m_blocks.size(); i++) 00773 { 00774 if (m_blocks[i]->m_name == other->m_name) 00775 { 00776 m_blocks[i]->DownRef(); 00777 m_blocks.replace(i, other); 00778 replaced = true; 00779 break; 00780 } 00781 } 00782 00783 if (!replaced) 00784 m_blocks << other; 00785 00786 if (forward) 00787 other->SetBlocks(this, false); 00788 } 00789 00792 void CommandLineArg::AllowOneOf(QList<CommandLineArg*> args) 00793 { 00794 // TODO: blocks do not get set properly if multiple dummy arguments 00795 // are provided. since this method will not have access to the 00796 // argument list, this issue will have to be resolved later in 00797 // ReconcileLinks(). 00798 QList<CommandLineArg*>::const_iterator i1,i2; 00799 00800 // loop through all but the last entry 00801 for (i1 = args.begin(); i1 != args.end()-1; ++i1) 00802 { 00803 // loop through the next to the last entry 00804 // and block use with the current 00805 for (i2 = i1+1; i2 != args.end(); ++i2) 00806 { 00807 (*i1)->SetBlocks(*i2); 00808 } 00809 00810 if ((*i1)->m_type == QVariant::Invalid) 00811 (*i1)->DownRef(); 00812 } 00813 } 00814 00821 void CommandLineArg::Convert(void) 00822 { 00823 if (!QCoreApplication::instance()) 00824 // QApplication not available, no sense doing anything yet 00825 return; 00826 00827 if (m_converted) 00828 // already run, abort 00829 return; 00830 00831 if (!m_given) 00832 { 00833 // nothing to work on, abort 00834 m_converted = true; 00835 return; 00836 } 00837 00838 if (m_type == QVariant::String) 00839 { 00840 if (m_stored.type() == QVariant::ByteArray) 00841 { 00842 m_stored = QString::fromLocal8Bit(m_stored.toByteArray()); 00843 } 00844 // else 00845 // not sure why this isnt a bytearray, but ignore it and 00846 // set it as converted 00847 } 00848 else if (m_type == QVariant::StringList) 00849 { 00850 if (m_stored.type() == QVariant::List) 00851 { 00852 QVariantList vlist = m_stored.toList(); 00853 QVariantList::const_iterator iter = vlist.begin(); 00854 QStringList slist; 00855 for (; iter != vlist.end(); ++iter) 00856 slist << QString::fromLocal8Bit(iter->toByteArray()); 00857 m_stored = QVariant(slist); 00858 } 00859 } 00860 else if (m_type == QVariant::Map) 00861 { 00862 QVariantMap vmap = m_stored.toMap(); 00863 QVariantMap::iterator iter = vmap.begin(); 00864 for (; iter != vmap.end(); ++iter) 00865 (*iter) = QString::fromLocal8Bit(iter->toByteArray()); 00866 } 00867 else 00868 return; 00869 00870 m_converted = true; 00871 } 00872 00873 00879 QString CommandLineArg::GetPreferredKeyword(void) const 00880 { 00881 QStringList::const_iterator it; 00882 QString preferred; 00883 int len = 0, len2; 00884 00885 for (it = m_keywords.constBegin(); it != m_keywords.constEnd(); ++it) 00886 { 00887 len2 = (*it).size(); 00888 if (len2 > len) 00889 { 00890 preferred = *it; 00891 len = len2; 00892 } 00893 } 00894 00895 return preferred; 00896 } 00897 00901 bool CommandLineArg::TestLinks(void) const 00902 { 00903 if (!m_given) 00904 return true; // not in use, no need for checks 00905 00906 QList<CommandLineArg*>::const_iterator i; 00907 00908 bool passes = false; 00909 for (i = m_parents.constBegin(); i != m_parents.constEnd(); ++i) 00910 { 00911 // one of these must have been defined 00912 if ((*i)->m_given) 00913 { 00914 passes = true; 00915 break; 00916 } 00917 } 00918 if (!passes && !m_parents.isEmpty()) 00919 { 00920 cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData() 00921 << " requires at least one of the following arguments" << endl; 00922 for (i = m_parents.constBegin(); i != m_parents.constEnd(); ++i) 00923 cerr << " " 00924 << (*i)->GetPreferredKeyword().toLocal8Bit().constData(); 00925 cerr << endl << endl; 00926 return false; 00927 } 00928 00929 // we dont care about children 00930 00931 for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i) 00932 { 00933 // all of these must have been defined 00934 if (!(*i)->m_given) 00935 { 00936 cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData() 00937 << " requires all of the following be defined as well" 00938 << endl; 00939 for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i) 00940 cerr << " " 00941 << (*i)->GetPreferredKeyword().toLocal8Bit() 00942 .constData(); 00943 cerr << endl << endl; 00944 return false; 00945 } 00946 } 00947 00948 for (i = m_blocks.constBegin(); i != m_blocks.constEnd(); ++i) 00949 { 00950 // none of these can be defined 00951 if ((*i)->m_given) 00952 { 00953 cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData() 00954 << " requires that none of the following be defined" << endl; 00955 for (i = m_blocks.constBegin(); i != m_blocks.constEnd(); ++i) 00956 cerr << " " 00957 << (*i)->GetPreferredKeyword().toLocal8Bit() 00958 .constData(); 00959 cerr << endl << endl; 00960 return false; 00961 } 00962 } 00963 00964 return true; 00965 } 00966 00969 void CommandLineArg::CleanupLinks(void) 00970 { 00971 // clear out interdependent pointers in preparation for deletion 00972 while (!m_parents.isEmpty()) 00973 m_parents.takeFirst()->DownRef(); 00974 00975 while (!m_children.isEmpty()) 00976 m_children.takeFirst()->DownRef(); 00977 00978 while (!m_blocks.isEmpty()) 00979 m_blocks.takeFirst()->DownRef(); 00980 00981 while (!m_requires.isEmpty()) 00982 m_requires.takeFirst()->DownRef(); 00983 00984 while (!m_requiredby.isEmpty()) 00985 m_requiredby.takeFirst()->DownRef(); 00986 } 00987 00990 void CommandLineArg::PrintVerbose(void) const 00991 { 00992 if (!m_given) 00993 return; 00994 00995 cerr << " " << m_name.leftJustified(30).toLocal8Bit().constData(); 00996 00997 QSize tmpsize; 00998 QMap<QString, QVariant> tmpmap; 00999 QMap<QString, QVariant>::const_iterator it; 01000 QVariantList vlist; 01001 QVariantList::const_iterator it2; 01002 bool first; 01003 01004 switch (m_type) 01005 { 01006 case QVariant::Bool: 01007 cerr << (m_stored.toBool() ? "True" : "False") << endl; 01008 break; 01009 01010 case QVariant::Int: 01011 cerr << m_stored.toInt() << endl; 01012 break; 01013 01014 case QVariant::UInt: 01015 cerr << m_stored.toUInt() << endl; 01016 break; 01017 01018 case QVariant::LongLong: 01019 cerr << m_stored.toLongLong() << endl; 01020 break; 01021 01022 case QVariant::Double: 01023 cerr << m_stored.toDouble() << endl; 01024 break; 01025 01026 case QVariant::Size: 01027 tmpsize = m_stored.toSize(); 01028 cerr << "x=" << tmpsize.width() 01029 << " y=" << tmpsize.height() 01030 << endl; 01031 break; 01032 01033 case QVariant::String: 01034 cerr << '"' << m_stored.toByteArray().constData() 01035 << '"' << endl; 01036 break; 01037 01038 case QVariant::StringList: 01039 vlist = m_stored.toList(); 01040 it2 = vlist.begin(); 01041 cerr << '"' << it2->toByteArray().constData() << '"'; 01042 ++it2; 01043 for (; it2 != vlist.end(); ++it2) 01044 cerr << ", \"" 01045 << it2->constData() 01046 << '"'; 01047 cerr << endl; 01048 break; 01049 01050 case QVariant::Map: 01051 tmpmap = m_stored.toMap(); 01052 first = true; 01053 01054 for (it = tmpmap.begin(); it != tmpmap.end(); ++it) 01055 { 01056 if (first) 01057 first = false; 01058 else 01059 cerr << QString("").leftJustified(32) 01060 .toLocal8Bit().constData(); 01061 01062 cerr << it.key().toLocal8Bit().constData() 01063 << '=' 01064 << it->toByteArray().constData() 01065 << endl; 01066 } 01067 01068 break; 01069 01070 case QVariant::DateTime: 01071 cerr << m_stored.toDateTime().toString(Qt::ISODate) 01072 .toLocal8Bit().constData() 01073 << endl; 01074 break; 01075 01076 default: 01077 cerr << endl; 01078 } 01079 } 01080 01083 void CommandLineArg::PrintRemovedWarning(QString &keyword) const 01084 { 01085 QString warn = QString("%1 has been removed").arg(keyword); 01086 if (!m_removedversion.isEmpty()) 01087 warn += QString(" as of MythTV %1").arg(m_removedversion); 01088 01089 cerr << QString("****************************************************\n" 01090 " WARNING: %1\n" 01091 " %2\n" 01092 "****************************************************\n\n") 01093 .arg(warn).arg(m_removed) 01094 .toLocal8Bit().constData(); 01095 } 01096 01099 void CommandLineArg::PrintDeprecatedWarning(QString &keyword) const 01100 { 01101 cerr << QString("****************************************************\n" 01102 " WARNING: %1 has been deprecated\n" 01103 " %2\n" 01104 "****************************************************\n\n") 01105 .arg(keyword).arg(m_deprecated) 01106 .toLocal8Bit().constData(); 01107 } 01108 01121 MythCommandLineParser::MythCommandLineParser(QString appname) : 01122 m_appname(appname), m_passthroughActive(false), 01123 m_overridesImported(false), m_verbose(false) 01124 { 01125 char *verbose = getenv("VERBOSE_PARSER"); 01126 if (verbose != NULL) 01127 { 01128 cerr << "MythCommandLineParser is now operating verbosely." << endl; 01129 m_verbose = true; 01130 } 01131 01132 LoadArguments(); 01133 } 01134 01135 MythCommandLineParser::~MythCommandLineParser() 01136 { 01137 QMap<QString, CommandLineArg*>::iterator i; 01138 01139 i = m_namedArgs.begin(); 01140 while (i != m_namedArgs.end()) 01141 { 01142 (*i)->CleanupLinks(); 01143 (*i)->DownRef(); 01144 i = m_namedArgs.erase(i); 01145 } 01146 01147 i = m_optionedArgs.begin(); 01148 while (i != m_optionedArgs.end()) 01149 { 01150 (*i)->DownRef(); 01151 i = m_optionedArgs.erase(i); 01152 } 01153 } 01154 01185 CommandLineArg* MythCommandLineParser::add(QStringList arglist, 01186 QString name, QVariant::Type type, QVariant def, 01187 QString help, QString longhelp) 01188 { 01189 CommandLineArg *arg; 01190 01191 if (m_namedArgs.contains(name)) 01192 arg = m_namedArgs[name]; 01193 else 01194 { 01195 arg = new CommandLineArg(name, type, def, help, longhelp); 01196 m_namedArgs.insert(name, arg); 01197 } 01198 01199 QStringList::const_iterator i; 01200 for (i = arglist.begin(); i != arglist.end(); ++i) 01201 { 01202 if (!m_optionedArgs.contains(*i)) 01203 { 01204 arg->AddKeyword(*i); 01205 if (m_verbose) 01206 cerr << "Adding " << (*i).toLocal8Bit().constData() 01207 << " as taking type '" << QVariant::typeToName(type) 01208 << "'" << endl; 01209 arg->UpRef(); 01210 m_optionedArgs.insert(*i, arg); 01211 } 01212 } 01213 01214 return arg; 01215 } 01216 01219 void MythCommandLineParser::PrintVersion(void) const 01220 { 01221 cout << "Please attach all output as a file in bug reports." << endl; 01222 cout << "MythTV Version : " << MYTH_SOURCE_VERSION << endl; 01223 cout << "MythTV Branch : " << MYTH_SOURCE_PATH << endl; 01224 cout << "Network Protocol : " << MYTH_PROTO_VERSION << endl; 01225 cout << "Library API : " << MYTH_BINARY_VERSION << endl; 01226 cout << "QT Version : " << QT_VERSION_STR << endl; 01227 #ifdef MYTH_BUILD_CONFIG 01228 cout << "Options compiled in:" <<endl; 01229 cout << MYTH_BUILD_CONFIG << endl; 01230 #endif 01231 } 01232 01235 void MythCommandLineParser::PrintHelp(void) const 01236 { 01237 QString help = GetHelpString(); 01238 cerr << help.toLocal8Bit().constData(); 01239 } 01240 01246 QString MythCommandLineParser::GetHelpString(void) const 01247 { 01248 QString helpstr; 01249 QTextStream msg(&helpstr, QIODevice::WriteOnly); 01250 01251 QString versionStr = QString("%1 version: %2 [%3] www.mythtv.org") 01252 .arg(m_appname).arg(MYTH_SOURCE_PATH).arg(MYTH_SOURCE_VERSION); 01253 msg << versionStr << endl; 01254 01255 if (toString("showhelp").isEmpty()) 01256 { 01257 // build generic help text 01258 01259 QString descr = GetHelpHeader(); 01260 if (descr.size() > 0) 01261 msg << endl << descr << endl << endl; 01262 01263 // loop through registered arguments to populate list of groups 01264 QStringList groups(""); 01265 int maxlen = 0; 01266 QMap<QString, CommandLineArg*>::const_iterator i1; 01267 for (i1 = m_namedArgs.begin(); i1 != m_namedArgs.end(); ++i1) 01268 { 01269 maxlen = max((*i1)->GetKeywordLength(), maxlen); 01270 if (!groups.contains((*i1)->m_group)) 01271 groups << (*i1)->m_group; 01272 } 01273 01274 // loop through list of groups and print help string for each 01275 // arguments will filter themselves if they are not in the group 01276 maxlen += 4; 01277 QStringList::const_iterator i2; 01278 for (i2 = groups.begin(); i2 != groups.end(); ++i2) 01279 { 01280 if ((*i2).isEmpty()) 01281 msg << "Misc. Options:" << endl; 01282 else 01283 msg << (*i2).toLocal8Bit().constData() << " Options:" << endl; 01284 01285 for (i1 = m_namedArgs.begin(); i1 != m_namedArgs.end(); ++i1) 01286 msg << (*i1)->GetHelpString(maxlen, *i2); 01287 msg << endl; 01288 } 01289 } 01290 else 01291 { 01292 // build help for a specific argument 01293 QString optstr = "-" + toString("showhelp"); 01294 if (!m_optionedArgs.contains(optstr)) 01295 { 01296 optstr = "-" + optstr; 01297 if (!m_optionedArgs.contains(optstr)) 01298 return QString("Could not find option matching '%1'\n") 01299 .arg(toString("showhelp")); 01300 } 01301 01302 msg << m_optionedArgs[optstr]->GetLongHelpString(optstr); 01303 } 01304 01305 msg.flush(); 01306 return helpstr; 01307 } 01308 01311 int MythCommandLineParser::getOpt(int argc, const char * const * argv, 01312 int &argpos, QString &opt, QByteArray &val) 01313 { 01314 opt.clear(); 01315 val.clear(); 01316 01317 if (argpos >= argc) 01318 // this shouldnt happen, return and exit 01319 return kEnd; 01320 01321 QByteArray tmp(argv[argpos]); 01322 if (tmp.isEmpty()) 01323 // string is empty, return and loop 01324 return kEmpty; 01325 01326 if (m_passthroughActive) 01327 { 01328 // pass through has been activated 01329 val = tmp; 01330 return kArg; 01331 } 01332 01333 if (tmp.startsWith('-') && tmp.size() > 1) 01334 { 01335 if (tmp == "--") 01336 { 01337 // all options beyond this will be passed as a single string 01338 m_passthroughActive = true; 01339 return kPassthrough; 01340 } 01341 01342 if (tmp.contains('=')) 01343 { 01344 // option contains '=', split 01345 QList<QByteArray> blist = tmp.split('='); 01346 01347 if (blist.size() != 2) 01348 { 01349 // more than one '=' in option, this is not handled 01350 opt = QString(tmp); 01351 return kInvalid; 01352 } 01353 01354 opt = QString(blist[0]); 01355 val = blist[1]; 01356 return kOptVal; 01357 } 01358 01359 opt = QString(tmp); 01360 01361 if (argpos+1 >= argc) 01362 // end of input, option only 01363 return kOptOnly; 01364 01365 tmp = QByteArray(argv[++argpos]); 01366 if (tmp.isEmpty()) 01367 // empty string, option only 01368 return kOptOnly; 01369 01370 if (tmp.startsWith("-") && tmp.size() > 1) 01371 { 01372 // no value found for option, backtrack 01373 argpos--; 01374 return kOptOnly; 01375 } 01376 01377 val = tmp; 01378 return kOptVal; 01379 } 01380 else 01381 { 01382 // input is not an option string, return as arg 01383 val = tmp; 01384 return kArg; 01385 } 01386 01387 } 01388 01395 bool MythCommandLineParser::Parse(int argc, const char * const * argv) 01396 { 01397 int res; 01398 QString opt; 01399 QByteArray val; 01400 CommandLineArg *argdef; 01401 01402 // reconnect interdependencies between command line options 01403 if (!ReconcileLinks()) 01404 return false; 01405 01406 // loop through command line arguments until all are spent 01407 for (int argpos = 1; argpos < argc; ++argpos) 01408 { 01409 01410 // pull next option 01411 res = getOpt(argc, argv, argpos, opt, val); 01412 01413 if (m_verbose) 01414 cerr << "res: " << NamedOptType(res) << endl 01415 << "opt: " << opt.toLocal8Bit().constData() << endl 01416 << "val: " << val.constData() << endl << endl; 01417 01418 // '--' found on command line, enable passthrough mode 01419 if (res == kPassthrough && !m_namedArgs.contains("_passthrough")) 01420 { 01421 cerr << "Received '--' but passthrough has not been enabled" << endl; 01422 SetValue("showhelp", ""); 01423 return false; 01424 } 01425 01426 // end of options found, terminate loop 01427 if (res == kEnd) 01428 break; 01429 01430 // GetOpt pulled an empty option, this shouldnt happen by ignore 01431 // it and continue 01432 else if (res == kEmpty) 01433 continue; 01434 01435 // more than one equal found in key/value pair, fault out 01436 else if (res == kInvalid) 01437 { 01438 cerr << "Invalid option received:" << endl << " " 01439 << opt.toLocal8Bit().constData(); 01440 SetValue("showhelp", ""); 01441 return false; 01442 } 01443 01444 // passthrough is active, so add the data to the stringlist 01445 else if (m_passthroughActive) 01446 { 01447 m_namedArgs["_passthrough"]->Set("", val); 01448 continue; 01449 } 01450 01451 // argument with no preceeding '-' encountered, add to stringlist 01452 else if (res == kArg) 01453 { 01454 if (!m_namedArgs.contains("_args")) 01455 { 01456 cerr << "Received '" 01457 << val.constData() 01458 << "' but unassociated arguments have not been enabled" 01459 << endl; 01460 SetValue("showhelp", ""); 01461 return false; 01462 } 01463 01464 m_namedArgs["_args"]->Set("", val); 01465 continue; 01466 } 01467 01468 // this line should not be passed once arguments have started collecting 01469 if (toBool("_args")) 01470 { 01471 cerr << "Command line arguments received out of sequence" 01472 << endl; 01473 SetValue("showhelp", ""); 01474 return false; 01475 } 01476 01477 #ifdef Q_WS_MACX 01478 if (opt.startsWith("-psn_")) 01479 { 01480 cerr << "Ignoring Process Serial Number from command line" 01481 << endl; 01482 continue; 01483 } 01484 #endif 01485 01486 if (!m_optionedArgs.contains(opt)) 01487 { 01488 // argument is unhandled, check if parser allows arbitrary input 01489 if (m_namedArgs.contains("_extra")) 01490 { 01491 // arbitrary allowed, specify general collection pool 01492 argdef = m_namedArgs["_extra"]; 01493 QByteArray tmp = opt.toLocal8Bit(); 01494 tmp += '='; 01495 tmp += val; 01496 val = tmp; 01497 res = kOptVal; 01498 } 01499 else 01500 { 01501 // arbitrary not allowed, fault out 01502 cerr << "Unhandled option given on command line:" << endl 01503 << " " << opt.toLocal8Bit().constData() << endl; 01504 SetValue("showhelp", ""); 01505 return false; 01506 } 01507 } 01508 else 01509 argdef = m_optionedArgs[opt]; 01510 01511 // argument has been marked as removed, warn user and fail 01512 if (!argdef->m_removed.isEmpty()) 01513 { 01514 argdef->PrintRemovedWarning(opt); 01515 SetValue("showhelp", ""); 01516 return false; 01517 } 01518 01519 // argument has been marked as deprecated, warn user 01520 if (!argdef->m_deprecated.isEmpty()) 01521 argdef->PrintDeprecatedWarning(opt); 01522 01523 if (m_verbose) 01524 cerr << "name: " << argdef->GetName().toLocal8Bit().constData() 01525 << endl; 01526 01527 // argument is keyword only, no value 01528 if (res == kOptOnly) 01529 { 01530 if (!argdef->Set(opt)) 01531 { 01532 SetValue("showhelp", ""); 01533 return false; 01534 } 01535 } 01536 // argument has keyword and value 01537 else if (res == kOptVal) 01538 { 01539 if (!argdef->Set(opt, val)) 01540 { 01541 // try testing keyword with no value 01542 if (!argdef->Set(opt)) 01543 { 01544 SetValue("showhelp", ""); 01545 return false; 01546 } 01547 // drop back an iteration so the unused value will get 01548 // processed a second time as a keyword-less argument 01549 --argpos; 01550 } 01551 } 01552 else 01553 { 01554 SetValue("showhelp", ""); 01555 return false; // this should not occur 01556 } 01557 01558 if (m_verbose) 01559 cerr << "value: " << argdef->m_stored.toString().toLocal8Bit().constData() 01560 << endl; 01561 } 01562 01563 QMap<QString, CommandLineArg*>::const_iterator it; 01564 01565 if (m_verbose) 01566 { 01567 cerr << "Processed option list:" << endl; 01568 for (it = m_namedArgs.begin(); it != m_namedArgs.end(); ++it) 01569 (*it)->PrintVerbose(); 01570 01571 if (m_namedArgs.contains("_args")) 01572 { 01573 cerr << endl << "Extra argument list:" << endl; 01574 QStringList slist = toStringList("_args"); 01575 QStringList::const_iterator it2 = slist.begin(); 01576 for (; it2 != slist.end(); ++it2) 01577 cerr << " " << (*it2).toLocal8Bit().constData() << endl; 01578 } 01579 01580 if (m_namedArgs.contains("_passthrough")) 01581 { 01582 cerr << endl << "Passthrough string:" << endl; 01583 cerr << " " << GetPassthrough().toLocal8Bit().constData() << endl; 01584 } 01585 01586 cerr << endl; 01587 } 01588 01589 // make sure all interdependencies are fulfilled 01590 for (it = m_namedArgs.begin(); it != m_namedArgs.end(); ++it) 01591 { 01592 if (!(*it)->TestLinks()) 01593 { 01594 QString keyword = (*it)->m_usedKeyword; 01595 if (keyword.startsWith('-')) 01596 { 01597 if (keyword.startsWith("--")) 01598 keyword.remove(0,2); 01599 else 01600 keyword.remove(0,1); 01601 } 01602 01603 SetValue("showhelp", keyword); 01604 return false; 01605 } 01606 } 01607 01608 return true; 01609 } 01610 01614 bool MythCommandLineParser::ReconcileLinks(void) 01615 { 01616 QList<CommandLineArg*> links; 01617 QMap<QString,CommandLineArg*>::iterator i1; 01618 QList<CommandLineArg*>::iterator i2; 01619 01620 if (m_verbose) 01621 cerr << "Reconciling links for option interdependencies." << endl; 01622 01623 for (i1 = m_namedArgs.begin(); i1 != m_namedArgs.end(); ++i1) 01624 { 01625 links = (*i1)->m_parents; 01626 for (i2 = links.begin(); i2 != links.end(); ++i2) 01627 { 01628 if ((*i2)->m_type != QVariant::Invalid) 01629 continue; // already handled 01630 01631 if (!m_namedArgs.contains((*i2)->m_name)) 01632 { 01633 // not found 01634 cerr << "ERROR: could not reconcile linked argument." << endl 01635 << " '" << (*i1)->m_name.toLocal8Bit().constData() 01636 << "' could not find '" 01637 << (*i2)->m_name.toLocal8Bit().constData() << "'." << endl 01638 << " Please resolve dependency and recompile." << endl; 01639 return false; 01640 } 01641 01642 // replace linked argument 01643 if (m_verbose) 01644 cerr << QString(" Setting %1 as child of %2") 01645 .arg((*i1)->m_name).arg((*i2)->m_name) 01646 .toLocal8Bit().constData() 01647 << endl; 01648 (*i1)->SetChildOf(m_namedArgs[(*i2)->m_name]); 01649 } 01650 01651 links = (*i1)->m_children; 01652 for (i2 = links.begin(); i2 != links.end(); ++i2) 01653 { 01654 if ((*i2)->m_type != QVariant::Invalid) 01655 continue; // already handled 01656 01657 if (!m_namedArgs.contains((*i2)->m_name)) 01658 { 01659 // not found 01660 cerr << "ERROR: could not reconcile linked argument." << endl 01661 << " '" << (*i1)->m_name.toLocal8Bit().constData() 01662 << "' could not find '" 01663 << (*i2)->m_name.toLocal8Bit().constData() << "'." << endl 01664 << " Please resolve dependency and recompile." << endl; 01665 return false; 01666 } 01667 01668 // replace linked argument 01669 if (m_verbose) 01670 cerr << QString(" Setting %1 as parent of %2") 01671 .arg((*i1)->m_name).arg((*i2)->m_name) 01672 .toLocal8Bit().constData() 01673 << endl; 01674 (*i1)->SetParentOf(m_namedArgs[(*i2)->m_name]); 01675 } 01676 01677 links = (*i1)->m_requires; 01678 for (i2 = links.begin(); i2 != links.end(); ++i2) 01679 { 01680 if ((*i2)->m_type != QVariant::Invalid) 01681 continue; // already handled 01682 01683 if (!m_namedArgs.contains((*i2)->m_name)) 01684 { 01685 // not found 01686 cerr << "ERROR: could not reconcile linked argument." << endl 01687 << " '" << (*i1)->m_name.toLocal8Bit().constData() 01688 << "' could not find '" 01689 << (*i2)->m_name.toLocal8Bit().constData() << "'." << endl 01690 << " Please resolve dependency and recompile." << endl; 01691 return false; 01692 } 01693 01694 // replace linked argument 01695 if (m_verbose) 01696 cerr << QString(" Setting %1 as requiring %2") 01697 .arg((*i1)->m_name).arg((*i2)->m_name) 01698 .toLocal8Bit().constData() 01699 << endl; 01700 (*i1)->SetRequires(m_namedArgs[(*i2)->m_name]); 01701 } 01702 01703 i2 = (*i1)->m_requiredby.begin(); 01704 while (i2 != (*i1)->m_requiredby.end()) 01705 { 01706 if ((*i2)->m_type == QVariant::Invalid) 01707 { 01708 // if its not an invalid, it shouldnt be here anyway 01709 if (m_namedArgs.contains((*i2)->m_name)) 01710 { 01711 m_namedArgs[(*i2)->m_name]->SetRequires(*i1); 01712 if (m_verbose) 01713 cerr << QString(" Setting %1 as blocking %2") 01714 .arg((*i1)->m_name).arg((*i2)->m_name) 01715 .toLocal8Bit().constData() 01716 << endl; 01717 } 01718 } 01719 01720 (*i2)->DownRef(); 01721 i2 = (*i1)->m_requiredby.erase(i2); 01722 } 01723 01724 i2 = (*i1)->m_blocks.begin(); 01725 while (i2 != (*i1)->m_blocks.end()) 01726 { 01727 if ((*i2)->m_type != QVariant::Invalid) 01728 { 01729 ++i2; 01730 continue; // already handled 01731 } 01732 01733 if (!m_namedArgs.contains((*i2)->m_name)) 01734 { 01735 (*i2)->DownRef(); 01736 i2 = (*i1)->m_blocks.erase(i2); 01737 continue; // if it doesnt exist, it cant block this command 01738 } 01739 01740 // replace linked argument 01741 if (m_verbose) 01742 cerr << QString(" Setting %1 as blocking %2") 01743 .arg((*i1)->m_name).arg((*i2)->m_name) 01744 .toLocal8Bit().constData() 01745 << endl; 01746 (*i1)->SetBlocks(m_namedArgs[(*i2)->m_name]); 01747 ++i2; 01748 } 01749 } 01750 01751 return true; 01752 } 01753 01757 QVariant MythCommandLineParser::operator[](const QString &name) 01758 { 01759 QVariant var(""); 01760 if (!m_namedArgs.contains(name)) 01761 return var; 01762 01763 CommandLineArg *arg = m_namedArgs[name]; 01764 01765 if (arg->m_given) 01766 var = arg->m_stored; 01767 else 01768 var = arg->m_default; 01769 01770 return var; 01771 } 01772 01776 QStringList MythCommandLineParser::GetArgs(void) const 01777 { 01778 return toStringList("_args"); 01779 } 01780 01784 QMap<QString,QString> MythCommandLineParser::GetExtra(void) const 01785 { 01786 return toMap("_extra"); 01787 } 01788 01791 QString MythCommandLineParser::GetPassthrough(void) const 01792 { 01793 return toStringList("_passthrough").join(" "); 01794 } 01795 01803 QMap<QString,QString> MythCommandLineParser::GetSettingsOverride(void) 01804 { 01805 QMap<QString,QString> smap = toMap("overridesettings"); 01806 01807 if (!m_overridesImported) 01808 { 01809 if (toBool("overridesettingsfile")) 01810 { 01811 QString filename = toString("overridesettingsfile"); 01812 if (!filename.isEmpty()) 01813 { 01814 QFile f(filename); 01815 if (f.open(QIODevice::ReadOnly)) 01816 { 01817 char buf[1024]; 01818 int64_t len = f.readLine(buf, sizeof(buf) - 1); 01819 while (len != -1) 01820 { 01821 if (len >= 1 && buf[len-1]=='\n') 01822 buf[len-1] = 0; 01823 QString line(buf); 01824 QStringList tokens = line.split("=", 01825 QString::SkipEmptyParts); 01826 if (tokens.size() == 2) 01827 { 01828 tokens[0].replace(QRegExp("^[\"']"), ""); 01829 tokens[0].replace(QRegExp("[\"']$"), ""); 01830 tokens[1].replace(QRegExp("^[\"']"), ""); 01831 tokens[1].replace(QRegExp("[\"']$"), ""); 01832 if (!tokens[0].isEmpty()) 01833 smap[tokens[0]] = tokens[1]; 01834 } 01835 len = f.readLine(buf, sizeof(buf) - 1); 01836 } 01837 } 01838 else 01839 { 01840 QByteArray tmp = filename.toAscii(); 01841 cerr << "Failed to open the override settings file: '" 01842 << tmp.constData() << "'" << endl; 01843 } 01844 } 01845 } 01846 01847 if (toBool("windowed")) 01848 smap["RunFrontendInWindow"] = "1"; 01849 else if (toBool("notwindowed")) 01850 smap["RunFrontendInWindow"] = "0"; 01851 01852 if (toBool("mousecursor")) 01853 smap["HideMouseCursor"] = "0"; 01854 else if (toBool("nomousecursor")) 01855 smap["HideMouseCursor"] = "1"; 01856 01857 m_overridesImported = true; 01858 01859 if (!smap.isEmpty()) 01860 { 01861 QVariantMap vmap; 01862 QMap<QString, QString>::const_iterator it; 01863 for (it = smap.begin(); it != smap.end(); ++it) 01864 vmap[it.key()] = QVariant(it.value()); 01865 01866 m_namedArgs["overridesettings"]->Set(QVariant(vmap)); 01867 } 01868 } 01869 01870 if (m_verbose) 01871 { 01872 cerr << "Option Overrides:" << endl; 01873 QMap<QString, QString>::const_iterator it; 01874 for (it = smap.constBegin(); it != smap.constEnd(); ++it) 01875 cerr << QString(" %1 - %2").arg(it.key(), 30).arg(*it) 01876 .toLocal8Bit().constData() << endl; 01877 } 01878 01879 return smap; 01880 } 01881 01888 bool MythCommandLineParser::toBool(QString key) const 01889 { 01890 if (!m_namedArgs.contains(key)) 01891 return false; 01892 01893 CommandLineArg *arg = m_namedArgs[key]; 01894 01895 if (arg->m_type == QVariant::Bool) 01896 { 01897 if (arg->m_given) 01898 return arg->m_stored.toBool(); 01899 return arg->m_default.toBool(); 01900 } 01901 01902 if (arg->m_given) 01903 return true; 01904 01905 return false; 01906 } 01907 01911 int MythCommandLineParser::toInt(QString key) const 01912 { 01913 int val = 0; 01914 if (!m_namedArgs.contains(key)) 01915 return val; 01916 01917 CommandLineArg *arg = m_namedArgs[key]; 01918 01919 if (arg->m_given) 01920 { 01921 if (arg->m_stored.canConvert(QVariant::Int)) 01922 val = arg->m_stored.toInt(); 01923 } 01924 else 01925 { 01926 if (arg->m_default.canConvert(QVariant::Int)) 01927 val = arg->m_default.toInt(); 01928 } 01929 01930 return val; 01931 } 01932 01936 uint MythCommandLineParser::toUInt(QString key) const 01937 { 01938 uint val = 0; 01939 if (!m_namedArgs.contains(key)) 01940 return val; 01941 01942 CommandLineArg *arg = m_namedArgs[key]; 01943 01944 if (arg->m_given) 01945 { 01946 if (arg->m_stored.canConvert(QVariant::UInt)) 01947 val = arg->m_stored.toUInt(); 01948 } 01949 else 01950 { 01951 if (arg->m_default.canConvert(QVariant::UInt)) 01952 val = arg->m_default.toUInt(); 01953 } 01954 01955 return val; 01956 } 01957 01961 long long MythCommandLineParser::toLongLong(QString key) const 01962 { 01963 long long val = 0; 01964 if (!m_namedArgs.contains(key)) 01965 return val; 01966 01967 CommandLineArg *arg = m_namedArgs[key]; 01968 01969 if (arg->m_given) 01970 { 01971 if (arg->m_stored.canConvert(QVariant::LongLong)) 01972 val = arg->m_stored.toLongLong(); 01973 } 01974 else 01975 { 01976 if (arg->m_default.canConvert(QVariant::LongLong)) 01977 val = arg->m_default.toLongLong(); 01978 } 01979 01980 return val; 01981 } 01982 01986 double MythCommandLineParser::toDouble(QString key) const 01987 { 01988 double val = 0.0; 01989 if (!m_namedArgs.contains(key)) 01990 return val; 01991 01992 CommandLineArg *arg = m_namedArgs[key]; 01993 01994 if (arg->m_given) 01995 { 01996 if (arg->m_stored.canConvert(QVariant::Double)) 01997 val = arg->m_stored.toDouble(); 01998 } 01999 else 02000 { 02001 if (arg->m_default.canConvert(QVariant::Double)) 02002 val = arg->m_default.toDouble(); 02003 } 02004 02005 return val; 02006 } 02007 02011 QSize MythCommandLineParser::toSize(QString key) const 02012 { 02013 QSize val(0,0); 02014 if (!m_namedArgs.contains(key)) 02015 return val; 02016 02017 CommandLineArg *arg = m_namedArgs[key]; 02018 02019 if (arg->m_given) 02020 { 02021 if (arg->m_stored.canConvert(QVariant::Size)) 02022 val = arg->m_stored.toSize(); 02023 } 02024 else 02025 { 02026 if (arg->m_default.canConvert(QVariant::Size)) 02027 val = arg->m_default.toSize(); 02028 } 02029 02030 return val; 02031 } 02032 02036 QString MythCommandLineParser::toString(QString key) const 02037 { 02038 QString val(""); 02039 if (!m_namedArgs.contains(key)) 02040 return val; 02041 02042 CommandLineArg *arg = m_namedArgs[key]; 02043 02044 if (arg->m_given) 02045 { 02046 if (!arg->m_converted) 02047 arg->Convert(); 02048 02049 if (arg->m_stored.canConvert(QVariant::String)) 02050 val = arg->m_stored.toString(); 02051 } 02052 else 02053 { 02054 if (arg->m_default.canConvert(QVariant::String)) 02055 val = arg->m_default.toString(); 02056 } 02057 02058 return val; 02059 } 02060 02065 QStringList MythCommandLineParser::toStringList(QString key, QString sep) const 02066 { 02067 QVariant varval; 02068 QStringList val; 02069 if (!m_namedArgs.contains(key)) 02070 return val; 02071 02072 CommandLineArg *arg = m_namedArgs[key]; 02073 02074 if (arg->m_given) 02075 { 02076 if (!arg->m_converted) 02077 arg->Convert(); 02078 02079 varval = arg->m_stored; 02080 } 02081 else 02082 varval = arg->m_default; 02083 02084 if (arg->m_type == QVariant::String && !sep.isEmpty()) 02085 val = varval.toString().split(sep); 02086 else if (varval.canConvert(QVariant::StringList)) 02087 val = varval.toStringList(); 02088 02089 return val; 02090 } 02091 02095 QMap<QString,QString> MythCommandLineParser::toMap(QString key) const 02096 { 02097 QMap<QString, QString> val; 02098 QMap<QString, QVariant> tmp; 02099 if (!m_namedArgs.contains(key)) 02100 return val; 02101 02102 CommandLineArg *arg = m_namedArgs[key]; 02103 02104 if (arg->m_given) 02105 { 02106 if (!arg->m_converted) 02107 arg->Convert(); 02108 02109 if (arg->m_stored.canConvert(QVariant::Map)) 02110 tmp = arg->m_stored.toMap(); 02111 } 02112 else 02113 { 02114 if (arg->m_default.canConvert(QVariant::Map)) 02115 tmp = arg->m_default.toMap(); 02116 } 02117 02118 QMap<QString, QVariant>::const_iterator i; 02119 for (i = tmp.begin(); i != tmp.end(); ++i) 02120 val[i.key()] = i.value().toString(); 02121 02122 return val; 02123 } 02124 02128 QDateTime MythCommandLineParser::toDateTime(QString key) const 02129 { 02130 QDateTime val; 02131 if (!m_namedArgs.contains(key)) 02132 return val; 02133 02134 CommandLineArg *arg = m_namedArgs[key]; 02135 02136 if (arg->m_given) 02137 { 02138 if (arg->m_stored.canConvert(QVariant::DateTime)) 02139 val = arg->m_stored.toDateTime(); 02140 } 02141 else 02142 { 02143 if (arg->m_default.canConvert(QVariant::DateTime)) 02144 val = arg->m_default.toDateTime(); 02145 } 02146 02147 return val; 02148 } 02149 02153 void MythCommandLineParser::allowArgs(bool allow) 02154 { 02155 if (m_namedArgs.contains("_args")) 02156 { 02157 if (!allow) 02158 m_namedArgs.remove("_args"); 02159 } 02160 else if (!allow) 02161 return; 02162 02163 CommandLineArg *arg = new CommandLineArg("_args", QVariant::StringList, 02164 QStringList()); 02165 m_namedArgs["_args"] = arg; 02166 } 02167 02171 void MythCommandLineParser::allowExtras(bool allow) 02172 { 02173 if (m_namedArgs.contains("_extra")) 02174 { 02175 if (!allow) 02176 m_namedArgs.remove("_extra"); 02177 } 02178 else if (!allow) 02179 return; 02180 02181 QMap<QString,QVariant> vmap; 02182 CommandLineArg *arg = new CommandLineArg("_extra", QVariant::Map, vmap); 02183 02184 m_namedArgs["_extra"] = arg; 02185 } 02186 02190 void MythCommandLineParser::allowPassthrough(bool allow) 02191 { 02192 if (m_namedArgs.contains("_passthrough")) 02193 { 02194 if (!allow) 02195 m_namedArgs.remove("_passthrough"); 02196 } 02197 else if (!allow) 02198 return; 02199 02200 CommandLineArg *arg = new CommandLineArg("_passthrough", 02201 QVariant::StringList, QStringList()); 02202 m_namedArgs["_passthrough"] = arg; 02203 } 02204 02207 void MythCommandLineParser::addHelp(void) 02208 { 02209 add(QStringList( QStringList() << "-h" << "--help" << "--usage" ), 02210 "showhelp", "", "Display this help printout, or give detailed " 02211 "information of selected option.", 02212 "Displays a list of all commands available for use with " 02213 "this application. If another option is provided as an " 02214 "argument, it will provide detailed information on that " 02215 "option."); 02216 } 02217 02220 void MythCommandLineParser::addVersion(void) 02221 { 02222 add("--version", "showversion", false, "Display version information.", 02223 "Display informtion about build, including:\n" 02224 " version, branch, protocol, library API, Qt " 02225 "and compiled options."); 02226 } 02227 02230 void MythCommandLineParser::addWindowed(void) 02231 { 02232 add(QStringList( QStringList() << "-nw" << "--no-windowed" ), 02233 "notwindowed", false, 02234 "Prevent application from running in a window.", "") 02235 ->SetBlocks("windowed") 02236 ->SetGroup("User Interface"); 02237 02238 add(QStringList( QStringList() << "-w" << "--windowed" ), "windowed", 02239 false, "Force application to run in a window.", "") 02240 ->SetGroup("User Interface"); 02241 } 02242 02245 void MythCommandLineParser::addMouse(void) 02246 { 02247 add("--mouse-cursor", "mousecursor", false, 02248 "Force visibility of the mouse cursor.", "") 02249 ->SetBlocks("nomousecursor") 02250 ->SetGroup("User Interface"); 02251 02252 add("--no-mouse-cursor", "nomousecursor", false, 02253 "Force the mouse cursor to be hidden.", "") 02254 ->SetGroup("User Interface"); 02255 } 02256 02259 void MythCommandLineParser::addDaemon(void) 02260 { 02261 add(QStringList( QStringList() << "-d" << "--daemon" ), "daemon", false, 02262 "Fork application into background after startup.", 02263 "Fork application into background, detatching from " 02264 "the local terminal.\nOften used with: " 02265 " --logpath --pidfile --user"); 02266 } 02267 02271 void MythCommandLineParser::addSettingsOverride(void) 02272 { 02273 add(QStringList( QStringList() << "-O" << "--override-setting" ), 02274 "overridesettings", QVariant::Map, 02275 "Override a single setting defined by a key=value pair.", 02276 "Override a single setting from the database using " 02277 "options defined as one or more key=value pairs\n" 02278 "Multiple can be defined by multiple uses of the " 02279 "-O option."); 02280 add("--override-settings-file", "overridesettingsfile", "", 02281 "Define a file of key=value pairs to be " 02282 "loaded for setting overrides.", ""); 02283 } 02284 02287 void MythCommandLineParser::addRecording(void) 02288 { 02289 add("--chanid", "chanid", 0U, 02290 "Specify chanid of recording to operate on.", "") 02291 ->SetRequires("starttime"); 02292 02293 add("--starttime", "starttime", QDateTime(), 02294 "Specify start time of recording to operate on.", ""); 02295 } 02296 02299 void MythCommandLineParser::addGeometry(void) 02300 { 02301 add(QStringList( QStringList() << "-geometry" << "--geometry" ), "geometry", 02302 "", "Specify window size and position (WxH[+X+Y])", "") 02303 ->SetGroup("User Interface"); 02304 } 02305 02308 void MythCommandLineParser::addDisplay(void) 02309 { 02310 #ifdef USING_X11 02311 add("-display", "display", "", "Specify X server to use.", "") 02312 ->SetGroup("User Interface"); 02313 #endif 02314 } 02315 02318 void MythCommandLineParser::addUPnP(void) 02319 { 02320 add("--noupnp", "noupnp", false, "Disable use of UPnP.", ""); 02321 } 02322 02327 void MythCommandLineParser::addLogging( 02328 const QString &defaultVerbosity, LogLevel_t defaultLogLevel) 02329 { 02330 defaultLogLevel = 02331 ((defaultLogLevel >= LOG_UNKNOWN) || (defaultLogLevel <= LOG_ANY)) ? 02332 LOG_INFO : defaultLogLevel; 02333 02334 QString logLevelStr = logLevelGetName(defaultLogLevel); 02335 02336 add(QStringList( QStringList() << "-v" << "--verbose" ), "verbose", 02337 defaultVerbosity, 02338 "Specify log filtering. Use '-v help' for level info.", "") 02339 ->SetGroup("Logging"); 02340 add("-V", "verboseint", 0U, "", 02341 "This option is intended for internal use only.\n" 02342 "This option takes an unsigned value corresponding " 02343 "to the bitwise log verbosity operator.") 02344 ->SetGroup("Logging"); 02345 add("--logpath", "logpath", "", 02346 "Writes logging messages to a file in the directory logpath with " 02347 "filenames in the format: applicationName.date.pid.log.\n" 02348 "This is typically used in combination with --daemon, and if used " 02349 "in combination with --pidfile, this can be used with log " 02350 "rotators, using the HUP call to inform MythTV to reload the " 02351 "file", "") 02352 ->SetGroup("Logging"); 02353 add(QStringList( QStringList() << "-q" << "--quiet"), "quiet", 0, 02354 "Don't log to the console (-q). Don't log anywhere (-q -q)", "") 02355 ->SetGroup("Logging"); 02356 add("--loglevel", "loglevel", logLevelStr, 02357 QString( 02358 "Set the logging level. All log messages at lower levels will be " 02359 "discarded.\n" 02360 "In descending order: emerg, alert, crit, err, warning, notice, " 02361 "info, debug\ndefaults to ") + logLevelStr, "") 02362 ->SetGroup("Logging"); 02363 add("--syslog", "syslog", "none", 02364 "Set the syslog logging facility.\nSet to \"none\" to disable, " 02365 "defaults to none.", "") 02366 ->SetGroup("Logging"); 02367 add("--nodblog", "nodblog", false, "Disable database logging.", "") 02368 ->SetGroup("Logging"); 02369 02370 add(QStringList( QStringList() << "-l" << "--logfile" ), 02371 "logfile", "", "", "") 02372 ->SetGroup("Logging") 02373 ->SetRemoved("This option has been removed as part of " 02374 "rewrite of the logging interface. Please update your init " 02375 "scripts to use --syslog to interface with your system's " 02376 "existing system logging daemon, or --logpath to specify a " 02377 "dirctory for MythTV to write its logs to.", "0.25"); 02378 } 02379 02382 void MythCommandLineParser::addPIDFile(void) 02383 { 02384 add(QStringList( QStringList() << "-p" << "--pidfile" ), "pidfile", "", 02385 "Write PID of application to filename.", 02386 "Write the PID of the currently running process as a single " 02387 "line to this file. Used for init scripts to know what " 02388 "process to terminate, and with log rotators " 02389 "to send a HUP signal to process to have it re-open files."); 02390 } 02391 02394 void MythCommandLineParser::addJob(void) 02395 { 02396 add(QStringList( QStringList() << "-j" << "--jobid" ), "jobid", 0, "", 02397 "Intended for internal use only, specify the JobID to match " 02398 "up with in the database for additional information and the " 02399 "ability to update runtime status in the database."); 02400 } 02401 02404 void MythCommandLineParser::addInFile(bool addOutFile) 02405 { 02406 add("--infile", "infile", "", "Input file URI", ""); 02407 if (addOutFile) 02408 add("--outfile", "outfile", "", "Output file URI", ""); 02409 } 02410 02413 QString MythCommandLineParser::GetLogFilePath(void) 02414 { 02415 QString logfile = toString("logpath"); 02416 pid_t pid = getpid(); 02417 02418 if (logfile.isEmpty()) 02419 return logfile; 02420 02421 QString logdir; 02422 QString filepath; 02423 02424 QFileInfo finfo(logfile); 02425 if (!finfo.isDir()) 02426 { 02427 LOG(VB_GENERAL, LOG_ERR, 02428 QString("%1 is not a directory, disabling logfiles") 02429 .arg(logfile)); 02430 return QString(); 02431 } 02432 02433 logdir = finfo.filePath(); 02434 logfile = QCoreApplication::applicationName() + "." + 02435 QDateTime::currentDateTime().toString("yyyyMMddhhmmss") + 02436 QString(".%1").arg(pid) + ".log"; 02437 02438 SetValue("logdir", logdir); 02439 SetValue("logfile", logfile); 02440 SetValue("filepath", QFileInfo(QDir(logdir), logfile).filePath()); 02441 02442 return toString("filepath"); 02443 } 02444 02447 int MythCommandLineParser::GetSyslogFacility(void) 02448 { 02449 QString setting = toString("syslog").toLower(); 02450 if (setting == "none") 02451 return -2; 02452 02453 return syslogGetFacility(setting); 02454 } 02455 02458 LogLevel_t MythCommandLineParser::GetLogLevel(void) 02459 { 02460 QString setting = toString("loglevel"); 02461 if (setting.isEmpty()) 02462 return LOG_INFO; 02463 02464 LogLevel_t level = logLevelGet(setting); 02465 if (level == LOG_UNKNOWN) 02466 cerr << "Unknown log level: " << setting.toLocal8Bit().constData() << 02467 endl; 02468 02469 return level; 02470 } 02471 02476 bool MythCommandLineParser::SetValue(const QString &key, QVariant value) 02477 { 02478 CommandLineArg *arg; 02479 02480 if (!m_namedArgs.contains(key)) 02481 { 02482 QVariant val(value); 02483 arg = new CommandLineArg(key, val.type(), val); 02484 m_namedArgs.insert(key, arg); 02485 } 02486 else 02487 { 02488 arg = m_namedArgs[key]; 02489 if (arg->m_type != value.type()) 02490 return false; 02491 } 02492 02493 arg->Set(value); 02494 return true; 02495 } 02496 02499 int MythCommandLineParser::ConfigureLogging(QString mask, unsigned int progress) 02500 { 02501 int err = 0; 02502 02503 // Setup the defaults 02504 verboseString = ""; 02505 verboseMask = 0; 02506 verboseArgParse(mask); 02507 02508 if (toBool("verbose")) 02509 { 02510 if ((err = verboseArgParse(toString("verbose")))) 02511 return err; 02512 } 02513 else if (toBool("verboseint")) 02514 verboseMask = toUInt("verboseint"); 02515 02516 verboseMask |= VB_STDIO|VB_FLUSH; 02517 02518 int quiet = toUInt("quiet"); 02519 if (max(quiet, (int)progress) > 1) 02520 { 02521 verboseMask = VB_NONE|VB_FLUSH; 02522 verboseArgParse("none"); 02523 } 02524 02525 int facility = GetSyslogFacility(); 02526 bool dblog = !toBool("nodblog"); 02527 LogLevel_t level = GetLogLevel(); 02528 if (level == LOG_UNKNOWN) 02529 return GENERIC_EXIT_INVALID_CMDLINE; 02530 02531 LOG(VB_GENERAL, LOG_CRIT, 02532 QString("%1 version: %2 [%3] www.mythtv.org") 02533 .arg(QCoreApplication::applicationName()) 02534 .arg(MYTH_SOURCE_PATH).arg(MYTH_SOURCE_VERSION)); 02535 LOG(VB_GENERAL, LOG_CRIT, QString("Qt version: compile: %1, runtime: %2") 02536 .arg(QT_VERSION_STR).arg(qVersion())); 02537 LOG(VB_GENERAL, LOG_NOTICE, 02538 QString("Enabled verbose msgs: %1").arg(verboseString)); 02539 02540 QString logfile = GetLogFilePath(); 02541 bool propagate = !logfile.isEmpty(); 02542 02543 if (toBool("daemon")) 02544 quiet = max(quiet, 1); 02545 02546 logStart(logfile, progress, quiet, facility, level, dblog, propagate); 02547 02548 return GENERIC_EXIT_OK; 02549 } 02550 02555 void MythCommandLineParser::ApplySettingsOverride(void) 02556 { 02557 if (m_verbose) 02558 cerr << "Applying settings override" << endl; 02559 02560 QMap<QString, QString> override = GetSettingsOverride(); 02561 if (override.size()) 02562 { 02563 QMap<QString, QString>::iterator it; 02564 for (it = override.begin(); it != override.end(); ++it) 02565 { 02566 LOG(VB_GENERAL, LOG_NOTICE, 02567 QString("Setting '%1' being forced to '%2'") 02568 .arg(it.key()).arg(*it)); 02569 gCoreContext->OverrideSettingForSession(it.key(), *it); 02570 } 02571 } 02572 } 02573 02574 bool openPidfile(ofstream &pidfs, const QString &pidfile) 02575 { 02576 if (!pidfile.isEmpty()) 02577 { 02578 pidfs.open(pidfile.toAscii().constData()); 02579 if (!pidfs) 02580 { 02581 cerr << "Could not open pid file: " << ENO_STR << endl; 02582 return false; 02583 } 02584 } 02585 return true; 02586 } 02587 02590 bool setUser(const QString &username) 02591 { 02592 if (username.isEmpty()) 02593 return true; 02594 02595 #ifdef _WIN32 02596 cerr << "--user option is not supported on Windows" << endl; 02597 return false; 02598 #else // ! _WIN32 02599 #if defined(__linux__) || defined(__LINUX__) 02600 // Check the current dumpability of core dumps, which will be disabled 02601 // by setuid, so we can re-enable, if appropriate 02602 int dumpability = prctl(PR_GET_DUMPABLE); 02603 #endif 02604 struct passwd *user_info = getpwnam(username.toLocal8Bit().constData()); 02605 const uid_t user_id = geteuid(); 02606 02607 if (user_id && (!user_info || user_id != user_info->pw_uid)) 02608 { 02609 cerr << "You must be running as root to use the --user switch." << endl; 02610 return false; 02611 } 02612 else if (user_info && user_id == user_info->pw_uid) 02613 { 02614 LOG(VB_GENERAL, LOG_WARNING, 02615 QString("Already running as '%1'").arg(username)); 02616 } 02617 else if (!user_id && user_info) 02618 { 02619 if (setenv("HOME", user_info->pw_dir,1) == -1) 02620 { 02621 cerr << "Error setting home directory." << endl; 02622 return false; 02623 } 02624 if (setgid(user_info->pw_gid) == -1) 02625 { 02626 cerr << "Error setting effective group." << endl; 02627 return false; 02628 } 02629 if (initgroups(user_info->pw_name, user_info->pw_gid) == -1) 02630 { 02631 cerr << "Error setting groups." << endl; 02632 return false; 02633 } 02634 if (setuid(user_info->pw_uid) == -1) 02635 { 02636 cerr << "Error setting effective user." << endl; 02637 return false; 02638 } 02639 #if defined(__linux__) || defined(__LINUX__) 02640 if (dumpability && (prctl(PR_SET_DUMPABLE, dumpability) == -1)) 02641 LOG(VB_GENERAL, LOG_WARNING, "Unable to re-enable core file " 02642 "creation. Run without the --user argument to use " 02643 "shell-specified limits."); 02644 #endif 02645 } 02646 else 02647 { 02648 cerr << QString("Invalid user '%1' specified with --user") 02649 .arg(username).toLocal8Bit().constData() << endl; 02650 return false; 02651 } 02652 return true; 02653 #endif // ! _WIN32 02654 } 02655 02656 02659 int MythCommandLineParser::Daemonize(void) 02660 { 02661 ofstream pidfs; 02662 if (!openPidfile(pidfs, toString("pidfile"))) 02663 return GENERIC_EXIT_PERMISSIONS_ERROR; 02664 02665 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) 02666 LOG(VB_GENERAL, LOG_WARNING, "Unable to ignore SIGPIPE"); 02667 02668 if (toBool("daemon") && (daemon(0, 1) < 0)) 02669 { 02670 cerr << "Failed to daemonize: " << ENO_STR << endl; 02671 return GENERIC_EXIT_DAEMONIZING_ERROR; 02672 } 02673 02674 QString username = toString("username"); 02675 if (!username.isEmpty() && !setUser(username)) 02676 return GENERIC_EXIT_PERMISSIONS_ERROR; 02677 02678 if (pidfs) 02679 { 02680 pidfs << getpid() << endl; 02681 pidfs.close(); 02682 } 02683 02684 return GENERIC_EXIT_OK; 02685 }
1.7.6.1