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 = std::chrono::seconds(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 = std::chrono::seconds(time(
nullptr) - bootTime.tv_sec);
95 uptime = std::chrono::seconds(::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")
251 .arg(
timeout.count()) .arg(host);
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)
269 .arg(duration_cast<std::chrono::seconds>(
timeout).count())
280 bool telnet(
const QString &host,
int port)
284 bool connected = s->ConnectToHost(host, port);
314 char *buf =
new char[buflen];
321 if (!dst.isWritable() && !dst.isOpen())
323 odst = dst.open(QIODevice::Unbuffered |
324 QIODevice::WriteOnly |
325 QIODevice::Truncate);
328 if (!src.isReadable() && !src.isOpen())
329 osrc = src.open(QIODevice::Unbuffered|QIODevice::ReadOnly);
331 bool ok = dst.isWritable() && src.isReadable();
332 long long total_bytes = 0LL;
336 long long rlen = src.read(buf, buflen);
339 LOG(VB_GENERAL, LOG_ERR,
"read error");
348 while ((rlen-off>0) && ok)
350 long long wlen = dst.write(buf + off, rlen - off);
355 LOG(VB_GENERAL, LOG_ERR,
"write error");
368 return (ok) ? total_bytes : -1LL;
377 char tempfilename[MAX_PATH] =
"";
380 if (GetTempFileNameA(
temppath,
"mth", 0, tempfilename))
385 unlink(tempfilename);
386 ret = mkdir(tempfilename);
389 ret = open(tempfilename, O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
391 QString tmpFileName(tempfilename);
393 QByteArray safe_name_template = name_template.toLatin1();
394 const char *
tmp = safe_name_template.constData();
395 char *ctemplate = strdup(
tmp);
399 ret = (mkdtemp(ctemplate)) ? 0 : -1;
403 mode_t cur_umask = umask(S_IRWXO | S_IRWXG);
404 ret = mkstemp(ctemplate);
408 QString tmpFileName(ctemplate);
414 LOG(VB_GENERAL, LOG_ERR, QString(
"createTempFile(%1), Error ")
415 .arg(name_template) +
ENO);
416 return name_template;
419 if (!dir && (ret >= 0))
445 QByteArray fname =
filename.toLatin1();
446 int ret = chmod(fname.constData(), 0666);
449 LOG(VB_GENERAL, LOG_ERR, QString(
"Unable to change permissions on file. (%1)").arg(
filename));
460 QByteArray
tmp = query.toLocal8Bit();
461 std::cout <<
tmp.constData();
463 tmp = def.toLocal8Bit();
465 std::cout <<
" [" <<
tmp.constData() <<
"] ";
469 if (!isatty(fileno(stdin)) || !isatty(fileno(
stdout)))
471 std::cout << std::endl <<
"[console is not interactive, using default '"
472 <<
tmp.constData() <<
"']" << std::endl;
476 QTextStream stream(stdin);
477 QString qresponse = stream.readLine();
479 if (qresponse.isEmpty())
490 QString str_resp =
getResponse(query, QString(
"%1").arg(def));
491 if (str_resp.isEmpty())
494 int resp = str_resp.toInt(&ok);
495 return (ok ? resp : def);
500 QStringList *intermediaries,
504 LOG(VB_GENERAL, LOG_DEBUG,
505 QString(
"getSymlinkTarget('%1', 0x%2, %3)")
506 .arg(start_file).arg((uint64_t)intermediaries,0,16)
511 QString cur_file = start_file;
512 QFileInfo fi(cur_file);
516 intermediaries->clear();
517 intermediaries->push_back(start_file);
520 for (
uint i = 0; (i <= maxLinks) && fi.isSymLink() &&
521 !(link = fi.symLinkTarget()).isEmpty(); i++)
523 cur_file = (link[0] ==
'/') ?
525 fi.absoluteDir().absolutePath() +
"/" + link;
527 if (intermediaries && !intermediaries->contains(cur_file))
528 intermediaries->push_back(cur_file);
530 fi = QFileInfo(cur_file);
536 for (
uint i = 0; i < intermediaries->size(); i++)
538 LOG(VB_GENERAL, LOG_DEBUG, QString(
" inter%1: %2")
539 .arg(i).arg((*intermediaries)[i]));
543 LOG(VB_GENERAL, LOG_DEBUG,
544 QString(
"getSymlinkTarget() -> '%1'")
545 .arg((!fi.isSymLink()) ? cur_file : QString()));
548 return (!fi.isSymLink()) ? cur_file : QString();
553 QStringList tokens = MAC.split(
':');
554 if (tokens.size() != 6)
556 LOG(VB_NETWORK, LOG_ERR,
557 QString(
"IsMACAddress(%1) = false, doesn't have 6 parts").arg(MAC));
561 for (
int y = 0; y < 6; y++)
563 if (tokens[y].isEmpty())
565 LOG(VB_NETWORK, LOG_ERR,
566 QString(
"IsMACAddress(%1) = false, part #%2 is empty.")
572 int value = tokens[y].toInt(&ok, 16);
575 LOG(VB_NETWORK, LOG_ERR,
576 QString(
"IsMACAddress(%1) = false, unable to "
577 "convert part '%2' to integer.")
578 .arg(MAC).arg(tokens[y]));
584 LOG(VB_NETWORK, LOG_ERR,
585 QString(
"IsMACAddress(%1) = false, part #%2 "
586 "evaluates to %3 which is higher than 255.")
587 .arg(MAC).arg(y).arg(value));
592 LOG(VB_NETWORK, LOG_DEBUG, QString(
"IsMACAddress(%1) = true").arg(MAC));
599 QFileInfo fileinfo(
file);
600 qint64 initialsize = fileinfo.size();
603 if (initialsize == 0)
604 return QString(
"NULL");
606 if (
file.open(QIODevice::ReadOnly))
610 LOG(VB_GENERAL, LOG_ERR,
611 "Error: Unable to open selected file, missing read permissions?");
612 return QString(
"NULL");
616 QDataStream stream(&
file);
617 stream.setByteOrder(QDataStream::LittleEndian);
618 for (quint64
tmp = 0, i = 0; i < 65536/
sizeof(
tmp); i++)
624 file.seek(initialsize - 65536);
625 for (quint64
tmp = 0, i = 0; i < 65536/
sizeof(
tmp); i++)
633 QString
output = QString(
"%1").arg(hash, 0, 16);
639 std::vector<char> msg(6,
static_cast<char>(0xFF));
640 std::array<char,6> macaddr {};
641 QStringList tokens = MAC.split(
':');
643 if (tokens.size() != 6)
645 LOG(VB_GENERAL, LOG_ERR,
646 QString(
"WakeOnLan(%1): Incorrect MAC length").arg(MAC));
650 for (
int y = 0; y < 6; y++)
653 macaddr[y] = tokens[y].toInt(&ok, 16);
657 LOG(VB_GENERAL, LOG_ERR,
658 QString(
"WakeOnLan(%1): Invalid MAC address").arg(MAC));
664 for (
int x = 0; x < 16; x++)
665 msg.insert(msg.end(), macaddr.cbegin(), macaddr.cend());
667 LOG(VB_NETWORK, LOG_INFO,
668 QString(
"WakeOnLan(): Sending WOL packet to %1").arg(MAC));
670 QUdpSocket udp_socket;
671 qlonglong msglen = msg.size();
672 return udp_socket.writeDatagram(
673 msg.data(), msglen, QHostAddress::Broadcast, 32767) == msglen;
692 #if CONFIG_DARWIN || (__FreeBSD__) || defined(__OpenBSD__)
693 const char *command =
"ps -ax | grep -i pulseaudio | grep -v grep > /dev/null";
695 const char *command =
"ps ch -C pulseaudio -o pid > /dev/null";
709 if ((-1 == ret) && (0 != errno) && (val >= 0))
711 LOG(VB_GENERAL, LOG_ERR,
"Failed to nice process" +
ENO);
720 #ifdef _POSIX_PRIORITY_SCHEDULING
745 #if defined(__linux__) && ( defined(__i386__) || defined(__ppc__) || \
746 defined(__x86_64__) || defined(__ia64__) )
753 #include <sys/ptrace.h>
754 #include <asm/unistd.h>
756 #if defined(__i386__)
757 # define NR_ioprio_set 289
758 # define NR_ioprio_get 290
759 #elif defined(__ppc__)
760 # define NR_ioprio_set 273
761 # define NR_ioprio_get 274
762 #elif defined(__x86_64__)
763 # define NR_ioprio_set 251
764 # define NR_ioprio_get 252
765 #elif defined(__ia64__)
766 # define NR_ioprio_set 1274
767 # define NR_ioprio_get 1275
770 #define IOPRIO_BITS (16)
771 #define IOPRIO_CLASS_SHIFT (13)
772 #define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
773 #define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
774 #define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
775 #define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | (data))
777 enum { IOPRIO_CLASS_NONE,IOPRIO_CLASS_RT,IOPRIO_CLASS_BE,IOPRIO_CLASS_IDLE, };
778 enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP, IOPRIO_WHO_USER, };
782 int new_ioclass = (val < 0) ? IOPRIO_CLASS_RT :
783 (val > 7) ? IOPRIO_CLASS_IDLE : IOPRIO_CLASS_BE;
784 int new_iodata = (new_ioclass == IOPRIO_CLASS_BE) ? val : 0;
785 int new_ioprio = IOPRIO_PRIO_VALUE(new_ioclass, new_iodata);
788 int old_ioprio = syscall(NR_ioprio_get, IOPRIO_WHO_PROCESS, pid);
789 if (old_ioprio == new_ioprio)
792 int ret = syscall(NR_ioprio_set, IOPRIO_WHO_PROCESS, pid, new_ioprio);
794 if (-1 == ret && EPERM == errno && IOPRIO_CLASS_BE != new_ioclass)
796 new_iodata = (new_ioclass == IOPRIO_CLASS_RT) ? 0 : 7;
797 new_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, new_iodata);
798 ret = syscall(NR_ioprio_set, IOPRIO_WHO_PROCESS, pid, new_ioprio);
815 QFileInfoList entries = aDir.entryInfoList(QDir::NoDotAndDotDot |
816 QDir::Dirs | QDir::Files);
817 int count = entries.size();
818 bool has_err =
false;
820 for (
int idx = 0; idx < count && !has_err; idx++)
822 QFileInfo entryInfo = entries[idx];
823 QString path = entryInfo.absoluteFilePath();
824 if (entryInfo.isDir())
837 if (!has_err && !aDir.rmdir(aDir.absolutePath()))
856 QString
LOC =
"setHttpProxy() - ";
859 QString var(qEnvironmentVariable(
"http_proxy"));
861 var = qEnvironmentVariable(
"HTTP_PROXY");
864 if (!var.startsWith(
"http://"))
865 var.prepend(
"http://");
867 QUrl url = QUrl(var, QUrl::TolerantMode);
868 QString host = url.host();
869 int port = url.port();
882 LOG(VB_NETWORK, LOG_INFO,
LOC +
883 QString(
"assuming port %1 on host %2") .arg(port).arg(host));
886 else if (!
ping(host, 1s))
888 LOG(VB_GENERAL, LOG_ERR,
LOC +
889 QString(
"cannot locate host %1").arg(host) +
890 "\n\t\t\tPlease check HTTP_PROXY environment variable!");
892 else if (!
telnet(host,port))
894 LOG(VB_GENERAL, LOG_ERR,
LOC +
895 QString(
"%1:%2 - cannot connect!").arg(host).arg(port) +
896 "\n\t\t\tPlease check HTTP_PROXY environment variable!");
900 LOG(VB_NETWORK, LOG_DEBUG,
LOC + QString(
"using http://%1:%2@%3:%4")
901 .arg(url.userName()).arg(url.password())
902 .arg(host).arg(port));
905 QNetworkProxy(QNetworkProxy::HttpCachingProxy,
906 host, port, url.userName(), url.password());
907 QNetworkProxy::setApplicationProxy(
p);
911 LOG(VB_NETWORK, LOG_DEBUG,
LOC +
"no HTTP_PROXY environment var.");
916 QNetworkProxyQuery query(QUrl(
"http://www.mythtv.org"));
918 proxies = QNetworkProxyFactory::systemProxyForQuery(query);
920 for (
const auto&
p : qAsConst(
proxies))
922 QString host =
p.hostName();
925 if (
p.type() == QNetworkProxy::NoProxy)
930 LOG(VB_NETWORK, LOG_ERR,
LOC +
931 "failed to contact proxy host " + host);
935 LOG(VB_NETWORK, LOG_INFO,
LOC + QString(
"using proxy host %1:%2")
936 .arg(host).arg(port));
937 QNetworkProxy::setApplicationProxy(
p);
943 if (!
p.user().isEmpty())
945 url =
"http://%1:%2@%3:%4",
946 url = url.arg(
p.user()).arg(
p.password());
950 url =
"http://%1:%2";
953 url = url.arg(
p.hostName()).arg(
p.port());
954 setenv(
"HTTP_PROXY", url.toLatin1(), 1);
955 setenv(
"http_proxy", url.toLatin1(), 0);
960 LOG(VB_NETWORK, LOG_ERR,
LOC +
"failed to find a network proxy");
967 width = std::max(width, 5);
969 for (
int i = 0; i < list.size(); i++)
971 QString
string = list.at(i);
973 if(
string.size() <= width )
976 QString left =
string.left(width);
977 bool inserted =
false;
979 while( !inserted && !left.endsWith(
" " ))
981 if(
string.mid(left.size(), 1) ==
" " )
983 list.replace(i, left);
984 list.insert(i+1,
string.mid(left.size()).trimmed());
990 if( !left.contains(
" ") )
993 list.replace(i, left +
"-");
994 list.insert(i+1,
string.mid(left.size()));
1003 list.replace(i, left);
1004 list.insert(i+1,
string.mid(left.size()).trimmed());
1011 static QReadWriteLock s_rwLock;
1012 static QMap<uint,QString> s_cache;
1014 s_rwLock.lockForRead();
1015 QMap<uint,QString>::const_iterator it = s_cache.constFind(level);
1016 if (it != s_cache.constEnd())
1025 for (
uint i = 0; i < level; i++)
1028 s_rwLock.lockForWrite();
1029 s_cache[level] = ret;
1035 int naturalCompare(
const QString &_a,
const QString &_b, Qt::CaseSensitivity caseSensitivity)
1055 if (caseSensitivity == Qt::CaseSensitive)
1066 const QChar* currA = a.unicode();
1067 const QChar* currB = b.unicode();
1074 while (!currA->isNull() && !currB->isNull())
1076 const QChar* begSeqA = currA;
1077 const QChar* begSeqB = currB;
1079 if (currA->unicode() == QChar::ObjectReplacementCharacter)
1084 if (currB->unicode() == QChar::ObjectReplacementCharacter)
1089 if (currA->unicode() == QChar::ReplacementCharacter)
1094 if (currB->unicode() == QChar::ReplacementCharacter)
1100 while (!currA->isNull() && !currA->isDigit() && !currA->isPunct() &&
1106 while (!currB->isNull() && !currB->isDigit() && !currB->isPunct() &&
1113 const QString& subA(a.mid(begSeqA - a.unicode(), currA - begSeqA));
1114 const QString& subB(b.mid(begSeqB - b.unicode(), currB - begSeqB));
1115 const int cmp = QString::localeAwareCompare(subA, subB);
1119 return cmp < 0 ? -1 : +1;
1122 if (currA->isNull() || currB->isNull())
1128 while ((currA->isPunct() || currA->isSpace()) &&
1129 (currB->isPunct() || currB->isSpace()))
1131 if (*currA != *currB)
1133 return (*currA < *currB) ? -1 : +1;
1137 if (currA->isNull() || currB->isNull())
1144 if ((*currA == QLatin1Char(
'0')) || (*currB == QLatin1Char(
'0')))
1150 if (!currA->isDigit() && !currB->isDigit())
1154 if (!currA->isDigit())
1158 if (!currB->isDigit())
1162 if (*currA < *currB)
1166 if (*currA > *currB)
1183 bool isFirstRun =
true;
1188 if (!currA->isDigit() && !currB->isDigit())
1196 if (!currA->isDigit())
1200 return *currA < *currB ? -1 : +1;
1204 if (!currB->isDigit())
1208 return *currA < *currB ? -1 : +1;
1212 if ((*currA < *currB) && (weight == 0))
1216 else if ((*currA > *currB) && (weight == 0))
1227 if (currA->isNull() && currB->isNull())
1232 return currA->isNull() ? -1 : + 1;
1237 return QTime::fromMSecsSinceStartOfDay(msecs.count()).toString(fmt);
1261 int tokenStart = -1;
1263 for (
int i = 0; i < line.size(); i++)
1265 const QChar c = line.at(i);
1270 if (c.isSpace())
break;
1272 else if (c ==
'\"') state =
INDQUOTE;
1273 else if (c ==
'\\') state =
ESCTEXT;
1278 fields += line.mid(tokenStart, i - tokenStart);
1282 else if (c ==
'\'') state =
INSQUOTE;
1283 else if (c ==
'\"') state =
INDQUOTE;
1284 else if (c ==
'\\') state =
ESCTEXT;
1287 if (c ==
'\'') state =
INTEXT;
1291 if (c ==
'\"') state =
INTEXT;
1301 fields += line.mid(tokenStart);