21 #include <sys/sysinfo.h>
26 #include <mach/mach.h>
30 #include <sys/mount.h>
31 #include <sys/sysctl.h>
36 #include <QReadWriteLock>
37 #include <QNetworkProxy>
38 #include <QStringList>
39 #include <QDataStream>
45 #include <QHostAddress>
46 #include <QDataStream>
47 #include <QRegularExpression>
48 #include <QRegularExpressionMatchIterator>
58 #include "mythconfig.h"
60 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
61 #define qEnvironmentVariable getenv
71 struct sysinfo sinfo {};
72 if (sysinfo(&sinfo) == -1)
74 LOG(VB_GENERAL, LOG_ERR,
"sysinfo() error");
77 uptime = sinfo.uptime;
79 #elif defined(__FreeBSD__) || CONFIG_DARWIN
81 std::array<int,2> mib { CTL_KERN, KERN_BOOTTIME };
82 struct timeval bootTime;
87 len =
sizeof(bootTime);
88 if (sysctl(mib.data(), 2, &bootTime, &len,
nullptr, 0) == -1)
90 LOG(VB_GENERAL, LOG_ERR,
"sysctl() error");
93 uptime = time(
nullptr) - bootTime.tv_sec;
95 uptime = ::GetTickCount() / 1000;
98 LOG(VB_GENERAL, LOG_NOTICE,
"Unknown platform. How do I get the uptime?");
111 bool getMemStats(
int &totalMB,
int &freeMB,
int &totalVM,
int &freeVM)
114 const size_t MB = (1024*1024);
115 struct sysinfo sinfo {};
116 if (sysinfo(&sinfo) == -1)
118 LOG(VB_GENERAL, LOG_ERR,
119 "getMemStats(): Error, sysinfo() call failed.");
123 totalMB = (int)((sinfo.totalram * sinfo.mem_unit)/MB);
124 freeMB = (int)((sinfo.freeram * sinfo.mem_unit)/MB);
125 totalVM = (int)((sinfo.totalswap * sinfo.mem_unit)/MB);
126 freeVM = (int)((sinfo.freeswap * sinfo.mem_unit)/MB);
130 mach_msg_type_number_t count;
132 vm_statistics_data_t s;
134 mp = mach_host_self();
137 if (host_page_size(mp, &pageSize) != KERN_SUCCESS)
140 count = HOST_VM_INFO_COUNT;
141 if (host_statistics(mp, HOST_VM_INFO,
142 (host_info_t)&s, &count) != KERN_SUCCESS)
144 LOG(VB_GENERAL, LOG_ERR,
"getMemStats(): Error, "
145 "failed to get virtual memory statistics.");
150 totalMB = (s.active_count + s.inactive_count +
151 s.wire_count + s.free_count) * pageSize / 1024;
152 freeMB = s.free_count * pageSize / 1024;
158 int64_t total, used, free;
160 totalVM = (int)(total >> 10);
161 freeVM = (int)(free >> 10);
180 #if !defined(_WIN32) && !defined(Q_OS_ANDROID)
182 if (
getloadavg(loads.data(), loads.size()) != -1)
197 const uchar *c = (uchar *) str;
205 if (*c > 0xC1 && *c < 0xF5)
207 int bytesToCheck = 2;
214 while (bytesToCheck--)
221 if (*c < 0x80 || *c > 0xBF)
250 QString cmd = QString(
"%systemroot%\\system32\\ping.exe -w %1 -n 1 %2>NUL")
258 QHostAddress addr = QHostAddress(addrstr);
259 #if defined(__FreeBSD__) || CONFIG_DARWIN
260 QString timeoutparam(
"-t");
263 QString timeoutparam(
"-w");
266 addr.protocol() == QAbstractSocket::IPv6Protocol ?
"ping6" :
"ping";
267 QString cmd = QString(
"%1 %2 %3 -c 1 %4 >/dev/null 2>&1")
268 .arg(pingcmd).arg(timeoutparam).arg(
timeout).arg(host);
278 bool telnet(
const QString &host,
int port)
282 bool connected = s->ConnectToHost(host, port);
312 char *buf =
new char[buflen];
319 if (!dst.isWritable() && !dst.isOpen())
321 odst = dst.open(QIODevice::Unbuffered |
322 QIODevice::WriteOnly |
323 QIODevice::Truncate);
326 if (!src.isReadable() && !src.isOpen())
327 osrc = src.open(QIODevice::Unbuffered|QIODevice::ReadOnly);
329 bool ok = dst.isWritable() && src.isReadable();
330 long long total_bytes = 0LL;
334 long long rlen = src.read(buf, buflen);
337 LOG(VB_GENERAL, LOG_ERR,
"read error");
346 while ((rlen-off>0) && ok)
348 long long wlen = dst.write(buf + off, rlen - off);
353 LOG(VB_GENERAL, LOG_ERR,
"write error");
366 return (ok) ? total_bytes : -1LL;
375 char tempfilename[MAX_PATH] =
"";
378 if (GetTempFileNameA(
temppath,
"mth", 0, tempfilename))
383 unlink(tempfilename);
384 ret = mkdir(tempfilename);
387 ret = open(tempfilename, O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
389 QString tmpFileName(tempfilename);
391 QByteArray safe_name_template = name_template.toLatin1();
392 const char *
tmp = safe_name_template.constData();
393 char *ctemplate = strdup(
tmp);
397 ret = (mkdtemp(ctemplate)) ? 0 : -1;
401 mode_t cur_umask = umask(S_IRWXO | S_IRWXG);
402 ret = mkstemp(ctemplate);
406 QString tmpFileName(ctemplate);
412 LOG(VB_GENERAL, LOG_ERR, QString(
"createTempFile(%1), Error ")
413 .
arg(name_template) +
ENO);
414 return name_template;
417 if (!
dir && (ret >= 0))
443 QByteArray fname =
filename.toLatin1();
444 int ret = chmod(fname.constData(), 0666);
447 LOG(VB_GENERAL, LOG_ERR, QString(
"Unable to change permissions on file. (%1)").
arg(
filename));
458 QByteArray
tmp =
query.toLocal8Bit();
459 std::cout <<
tmp.constData();
461 tmp = def.toLocal8Bit();
463 std::cout <<
" [" <<
tmp.constData() <<
"] ";
467 if (!isatty(fileno(stdin)) || !isatty(fileno(
stdout)))
469 std::cout << std::endl <<
"[console is not interactive, using default '"
470 <<
tmp.constData() <<
"']" << std::endl;
474 QTextStream stream(stdin);
475 QString qresponse = stream.readLine();
477 if (qresponse.isEmpty())
489 if (str_resp.isEmpty())
492 int resp = str_resp.toInt(&ok);
493 return (ok ? resp : def);
498 QStringList *intermediaries,
502 LOG(VB_GENERAL, LOG_DEBUG,
503 QString(
"getSymlinkTarget('%1', 0x%2, %3)")
504 .
arg(start_file).
arg((uint64_t)intermediaries,0,16)
509 QString cur_file = start_file;
510 QFileInfo fi(cur_file);
514 intermediaries->clear();
515 intermediaries->push_back(start_file);
518 for (
uint i = 0; (i <= maxLinks) && fi.isSymLink() &&
519 !(link = fi.symLinkTarget()).isEmpty(); i++)
521 cur_file = (link[0] ==
'/') ?
523 fi.absoluteDir().absolutePath() +
"/" + link;
525 if (intermediaries && !intermediaries->contains(cur_file))
526 intermediaries->push_back(cur_file);
528 fi = QFileInfo(cur_file);
534 for (
uint i = 0; i < intermediaries->size(); i++)
536 LOG(VB_GENERAL, LOG_DEBUG, QString(
" inter%1: %2")
537 .
arg(i).
arg((*intermediaries)[i]));
541 LOG(VB_GENERAL, LOG_DEBUG,
542 QString(
"getSymlinkTarget() -> '%1'")
543 .
arg((!fi.isSymLink()) ? cur_file : QString()));
546 return (!fi.isSymLink()) ? cur_file : QString();
551 QStringList tokens = MAC.split(
':');
552 if (tokens.size() != 6)
554 LOG(VB_NETWORK, LOG_ERR,
555 QString(
"IsMACAddress(%1) = false, doesn't have 6 parts").
arg(MAC));
559 for (
int y = 0; y < 6; y++)
561 if (tokens[y].isEmpty())
563 LOG(VB_NETWORK, LOG_ERR,
564 QString(
"IsMACAddress(%1) = false, part #%2 is empty.")
570 int value = tokens[y].toInt(&ok, 16);
573 LOG(VB_NETWORK, LOG_ERR,
574 QString(
"IsMACAddress(%1) = false, unable to "
575 "convert part '%2' to integer.")
576 .
arg(MAC).
arg(tokens[y]));
582 LOG(VB_NETWORK, LOG_ERR,
583 QString(
"IsMACAddress(%1) = false, part #%2 "
584 "evaluates to %3 which is higher than 255.")
590 LOG(VB_NETWORK, LOG_DEBUG, QString(
"IsMACAddress(%1) = true").
arg(MAC));
597 QFileInfo fileinfo(
file);
598 qint64 initialsize = fileinfo.size();
601 if (initialsize == 0)
602 return QString(
"NULL");
604 if (
file.open(QIODevice::ReadOnly))
608 LOG(VB_GENERAL, LOG_ERR,
609 "Error: Unable to open selected file, missing read permissions?");
610 return QString(
"NULL");
614 QDataStream stream(&
file);
615 stream.setByteOrder(QDataStream::LittleEndian);
616 for (quint64
tmp = 0, i = 0; i < 65536/
sizeof(
tmp); i++)
622 file.seek(initialsize - 65536);
623 for (quint64
tmp = 0, i = 0; i < 65536/
sizeof(
tmp); i++)
631 QString
output = QString(
"%1").arg(hash, 0, 16);
637 std::vector<char> msg(6,
static_cast<char>(0xFF));
638 std::array<char,6> macaddr {};
639 QStringList tokens = MAC.split(
':');
641 if (tokens.size() != 6)
643 LOG(VB_GENERAL, LOG_ERR,
644 QString(
"WakeOnLan(%1): Incorrect MAC length").
arg(MAC));
648 for (
int y = 0; y < 6; y++)
651 macaddr[y] = tokens[y].toInt(&ok, 16);
655 LOG(VB_GENERAL, LOG_ERR,
656 QString(
"WakeOnLan(%1): Invalid MAC address").
arg(MAC));
662 for (
int x = 0; x < 16; x++)
663 msg.insert(msg.end(), macaddr.cbegin(), macaddr.cend());
665 LOG(VB_NETWORK, LOG_INFO,
666 QString(
"WakeOnLan(): Sending WOL packet to %1").
arg(MAC));
668 QUdpSocket udp_socket;
669 qlonglong msglen = msg.size();
670 return udp_socket.writeDatagram(
671 msg.data(), msglen, QHostAddress::Broadcast, 32767) == msglen;
690 #if CONFIG_DARWIN || (__FreeBSD__) || defined(__OpenBSD__)
691 const char *command =
"ps -ax | grep -i pulseaudio | grep -v grep > /dev/null";
693 const char *command =
"ps ch -C pulseaudio -o pid > /dev/null";
707 if ((-1 == ret) && (0 != errno) && (val >= 0))
709 LOG(VB_GENERAL, LOG_ERR,
"Failed to nice process" +
ENO);
718 #ifdef _POSIX_PRIORITY_SCHEDULING
743 #if defined(__linux__) && ( defined(__i386__) || defined(__ppc__) || \
744 defined(__x86_64__) || defined(__ia64__) )
751 #include <sys/ptrace.h>
752 #include <asm/unistd.h>
754 #if defined(__i386__)
755 # define NR_ioprio_set 289
756 # define NR_ioprio_get 290
757 #elif defined(__ppc__)
758 # define NR_ioprio_set 273
759 # define NR_ioprio_get 274
760 #elif defined(__x86_64__)
761 # define NR_ioprio_set 251
762 # define NR_ioprio_get 252
763 #elif defined(__ia64__)
764 # define NR_ioprio_set 1274
765 # define NR_ioprio_get 1275
768 #define IOPRIO_BITS (16)
769 #define IOPRIO_CLASS_SHIFT (13)
770 #define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
771 #define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
772 #define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
773 #define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | (data))
775 enum { IOPRIO_CLASS_NONE,IOPRIO_CLASS_RT,IOPRIO_CLASS_BE,IOPRIO_CLASS_IDLE, };
776 enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP, IOPRIO_WHO_USER, };
780 int new_ioclass = (val < 0) ? IOPRIO_CLASS_RT :
781 (val > 7) ? IOPRIO_CLASS_IDLE : IOPRIO_CLASS_BE;
782 int new_iodata = (new_ioclass == IOPRIO_CLASS_BE) ? val : 0;
783 int new_ioprio = IOPRIO_PRIO_VALUE(new_ioclass, new_iodata);
786 int old_ioprio = syscall(NR_ioprio_get, IOPRIO_WHO_PROCESS, pid);
787 if (old_ioprio == new_ioprio)
790 int ret = syscall(NR_ioprio_set, IOPRIO_WHO_PROCESS, pid, new_ioprio);
792 if (-1 == ret && EPERM == errno && IOPRIO_CLASS_BE != new_ioclass)
794 new_iodata = (new_ioclass == IOPRIO_CLASS_RT) ? 0 : 7;
795 new_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, new_iodata);
796 ret = syscall(NR_ioprio_set, IOPRIO_WHO_PROCESS, pid, new_ioprio);
813 QFileInfoList entries = aDir.entryInfoList(QDir::NoDotAndDotDot |
814 QDir::Dirs | QDir::Files);
815 int count = entries.size();
816 bool has_err =
false;
818 for (
int idx = 0; idx < count && !has_err; idx++)
820 QFileInfo entryInfo = entries[idx];
821 QString path = entryInfo.absoluteFilePath();
822 if (entryInfo.isDir())
835 if (!has_err && !aDir.rmdir(aDir.absolutePath()))
854 QString
LOC =
"setHttpProxy() - ";
857 QString var(qEnvironmentVariable(
"http_proxy"));
859 var = qEnvironmentVariable(
"HTTP_PROXY");
862 if (!var.startsWith(
"http://"))
863 var.prepend(
"http://");
865 QUrl url = QUrl(var, QUrl::TolerantMode);
866 QString host = url.host();
867 int port = url.port();
880 LOG(VB_NETWORK, LOG_INFO,
LOC +
881 QString(
"assuming port %1 on host %2") .
arg(port).
arg(host));
884 else if (!
ping(host, 1))
886 LOG(VB_GENERAL, LOG_ERR,
LOC +
887 QString(
"cannot locate host %1").
arg(host) +
888 "\n\t\t\tPlease check HTTP_PROXY environment variable!");
890 else if (!
telnet(host,port))
892 LOG(VB_GENERAL, LOG_ERR,
LOC +
893 QString(
"%1:%2 - cannot connect!").
arg(host).
arg(port) +
894 "\n\t\t\tPlease check HTTP_PROXY environment variable!");
898 LOG(VB_NETWORK, LOG_DEBUG,
LOC + QString(
"using http://%1:%2@%3:%4")
899 .
arg(url.userName()).arg(url.password())
900 .arg(host).arg(port));
903 QNetworkProxy(QNetworkProxy::HttpCachingProxy,
904 host, port, url.userName(), url.password());
905 QNetworkProxy::setApplicationProxy(
p);
909 LOG(VB_NETWORK, LOG_DEBUG,
LOC +
"no HTTP_PROXY environment var.");
914 QNetworkProxyQuery
query(QUrl(
"http://www.mythtv.org"));
916 proxies = QNetworkProxyFactory::systemProxyForQuery(
query);
918 for (
const auto&
p : qAsConst(
proxies))
920 QString host =
p.hostName();
923 if (
p.type() == QNetworkProxy::NoProxy)
928 LOG(VB_NETWORK, LOG_ERR,
LOC +
929 "failed to contact proxy host " + host);
933 LOG(VB_NETWORK, LOG_INFO,
LOC + QString(
"using proxy host %1:%2")
935 QNetworkProxy::setApplicationProxy(
p);
941 if (!
p.user().isEmpty())
943 url =
"http://%1:%2@%3:%4",
944 url = url.arg(
p.user()).arg(
p.password());
948 url =
"http://%1:%2";
951 url = url.arg(
p.hostName()).arg(
p.port());
952 setenv(
"HTTP_PROXY", url.toLatin1(), 1);
953 setenv(
"http_proxy", url.toLatin1(), 0);
958 LOG(VB_NETWORK, LOG_ERR,
LOC +
"failed to find a network proxy");
965 width = std::max(width, 5);
967 for (
int i = 0; i < list.size(); i++)
969 QString
string = list.at(i);
971 if(
string.size() <= width )
974 QString left =
string.left(width);
975 bool inserted =
false;
977 while( !inserted && !left.endsWith(
" " ))
979 if(
string.mid(left.size(), 1) ==
" " )
981 list.replace(i, left);
982 list.insert(i+1,
string.mid(left.size()).trimmed());
988 if( !left.contains(
" ") )
991 list.replace(i, left +
"-");
992 list.insert(i+1,
string.mid(left.size()));
1001 list.replace(i, left);
1002 list.insert(i+1,
string.mid(left.size()).trimmed());
1009 static QReadWriteLock s_rwLock;
1010 static QMap<uint,QString> s_cache;
1012 s_rwLock.lockForRead();
1013 QMap<uint,QString>::const_iterator it = s_cache.constFind(level);
1014 if (it != s_cache.constEnd())
1023 for (
uint i = 0; i < level; i++)
1026 s_rwLock.lockForWrite();
1027 s_cache[level] = ret;
1033 int naturalCompare(
const QString &_a,
const QString &_b, Qt::CaseSensitivity caseSensitivity)
1053 if (caseSensitivity == Qt::CaseSensitive)
1064 const QChar* currA = a.unicode();
1065 const QChar* currB = b.unicode();
1072 while (!currA->isNull() && !currB->isNull())
1074 const QChar* begSeqA = currA;
1075 const QChar* begSeqB = currB;
1077 if (currA->unicode() == QChar::ObjectReplacementCharacter)
1082 if (currB->unicode() == QChar::ObjectReplacementCharacter)
1087 if (currA->unicode() == QChar::ReplacementCharacter)
1092 if (currB->unicode() == QChar::ReplacementCharacter)
1098 while (!currA->isNull() && !currA->isDigit() && !currA->isPunct() &&
1104 while (!currB->isNull() && !currB->isDigit() && !currB->isPunct() &&
1111 const QStringRef& subA(a.midRef(begSeqA - a.unicode(), currA - begSeqA));
1112 const QStringRef& subB(b.midRef(begSeqB - b.unicode(), currB - begSeqB));
1113 const int cmp = QStringRef::localeAwareCompare(subA, subB);
1117 return cmp < 0 ? -1 : +1;
1120 if (currA->isNull() || currB->isNull())
1126 while ((currA->isPunct() || currA->isSpace()) &&
1127 (currB->isPunct() || currB->isSpace()))
1129 if (*currA != *currB)
1131 return (*currA < *currB) ? -1 : +1;
1135 if (currA->isNull() || currB->isNull())
1142 if ((*currA == QLatin1Char(
'0')) || (*currB == QLatin1Char(
'0')))
1148 if (!currA->isDigit() && !currB->isDigit())
1152 if (!currA->isDigit())
1156 if (!currB->isDigit())
1160 if (*currA < *currB)
1164 if (*currA > *currB)
1181 bool isFirstRun =
true;
1186 if (!currA->isDigit() && !currB->isDigit())
1194 if (!currA->isDigit())
1198 return *currA < *currB ? -1 : +1;
1202 if (!currB->isDigit())
1206 return *currA < *currB ? -1 : +1;
1210 if ((*currA < *currB) && (weight == 0))
1214 else if ((*currA > *currB) && (weight == 0))
1225 if (currA->isNull() && currB->isNull())
1230 return currA->isNull() ? -1 : + 1;
1235 return QTime::fromMSecsSinceStartOfDay(msecs).toString(fmt);
1240 return QTime::fromMSecsSinceStartOfDay(secs*1000).toString(fmt);
1264 int tokenStart = -1;
1266 for (
int i = 0; i < line.size(); i++)
1268 const QChar c = line.at(i);
1273 if (c.isSpace())
break;
1275 else if (c ==
'\"') state =
INDQUOTE;
1276 else if (c ==
'\\') state =
ESCTEXT;
1281 fields += line.mid(tokenStart, i - tokenStart);
1285 else if (c ==
'\'') state =
INSQUOTE;
1286 else if (c ==
'\"') state =
INDQUOTE;
1287 else if (c ==
'\\') state =
ESCTEXT;
1290 if (c ==
'\'') state =
INTEXT;
1294 if (c ==
'\"') state =
INTEXT;
1304 fields += line.mid(tokenStart);