MythTV  master
mythmiscutil.cpp
Go to the documentation of this file.
1 
2 #include "mythmiscutil.h"
3 
4 // C++ headers
5 #include <array>
6 #include <cerrno>
7 #include <cstdlib>
8 #include <ctime>
9 #include <iostream>
10 
11 // POSIX
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <sched.h>
15 
16 // System specific C headers
17 #include "compat.h"
18 
19 #ifdef linux
20 #include <sys/vfs.h>
21 #include <sys/sysinfo.h>
22 #include <sys/stat.h> // for umask, chmod
23 #endif
24 
25 #if CONFIG_DARWIN
26 #include <mach/mach.h>
27 #endif
28 
29 #ifdef BSD
30 #include <sys/mount.h> // for struct statfs
31 #include <sys/sysctl.h>
32 #include <sys/stat.h> // for umask, chmod
33 #endif
34 
35 // Qt headers
36 #include <QReadWriteLock>
37 #include <QNetworkProxy>
38 #include <QStringList>
39 #include <QDataStream>
40 #include <QUdpSocket>
41 #include <QFileInfo>
42 #include <QFile>
43 #include <QDir>
44 #include <QUrl>
45 #include <QHostAddress>
46 #include <QDataStream>
47 #include <QRegularExpression>
48 #include <QRegularExpressionMatchIterator>
49 
50 // Myth headers
51 #include "mythcorecontext.h"
52 #include "exitcodes.h"
53 #include "mythlogging.h"
54 #include "mythsocket.h"
55 #include "mythcoreutil.h"
56 #include "mythsystemlegacy.h"
57 
58 #include "mythconfig.h" // for CONFIG_DARWIN
59 
60 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
61 #define qEnvironmentVariable getenv
62 #endif
63 
68 bool getUptime(time_t &uptime)
69 {
70 #ifdef __linux__
71  struct sysinfo sinfo {};
72  if (sysinfo(&sinfo) == -1)
73  {
74  LOG(VB_GENERAL, LOG_ERR, "sysinfo() error");
75  return false;
76  }
77  uptime = sinfo.uptime;
78 
79 #elif defined(__FreeBSD__) || CONFIG_DARWIN
80 
81  std::array<int,2> mib { CTL_KERN, KERN_BOOTTIME };
82  struct timeval bootTime;
83  size_t len;
84 
85  // Uptime is calculated. Get this machine's boot time
86  // and subtract it from the current machine time
87  len = sizeof(bootTime);
88  if (sysctl(mib.data(), 2, &bootTime, &len, nullptr, 0) == -1)
89  {
90  LOG(VB_GENERAL, LOG_ERR, "sysctl() error");
91  return false;
92  }
93  uptime = time(nullptr) - bootTime.tv_sec;
94 #elif defined(_WIN32)
95  uptime = ::GetTickCount() / 1000;
96 #else
97  // Hmmm. Not Linux, not FreeBSD or Darwin. What else is there :-)
98  LOG(VB_GENERAL, LOG_NOTICE, "Unknown platform. How do I get the uptime?");
99  return false;
100 #endif
101 
102  return true;
103 }
104 
111 bool getMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
112 {
113 #ifdef __linux__
114  const size_t MB = (1024*1024);
115  struct sysinfo sinfo {};
116  if (sysinfo(&sinfo) == -1)
117  {
118  LOG(VB_GENERAL, LOG_ERR,
119  "getMemStats(): Error, sysinfo() call failed.");
120  return false;
121  }
122 
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);
127  return true;
128 #elif CONFIG_DARWIN
129  mach_port_t mp;
130  mach_msg_type_number_t count;
131  vm_size_t pageSize;
132  vm_statistics_data_t s;
133 
134  mp = mach_host_self();
135 
136  // VM page size
137  if (host_page_size(mp, &pageSize) != KERN_SUCCESS)
138  pageSize = 4096; // If we can't look it up, 4K is a good guess
139 
140  count = HOST_VM_INFO_COUNT;
141  if (host_statistics(mp, HOST_VM_INFO,
142  (host_info_t)&s, &count) != KERN_SUCCESS)
143  {
144  LOG(VB_GENERAL, LOG_ERR, "getMemStats(): Error, "
145  "failed to get virtual memory statistics.");
146  return false;
147  }
148 
149  pageSize >>= 10; // This gives usages in KB
150  totalMB = (s.active_count + s.inactive_count +
151  s.wire_count + s.free_count) * pageSize / 1024;
152  freeMB = s.free_count * pageSize / 1024;
153 
154 
155  // This is a real hack. I have not found a way to ask the kernel how much
156  // swap it is using, and the dynamic_pager daemon doesn't even seem to be
157  // able to report what filesystem it is using for the swapfiles. So, we do:
158  int64_t total, used, free;
159  free = getDiskSpace("/private/var/vm", total, used);
160  totalVM = (int)(total >> 10);
161  freeVM = (int)(free >> 10);
162  return true;
163 #else
164  Q_UNUSED(totalMB);
165  Q_UNUSED(freeMB);
166  Q_UNUSED(totalVM);
167  Q_UNUSED(freeVM);
168  return false;
169 #endif
170 }
171 
179 {
180 #if !defined(_WIN32) && !defined(Q_OS_ANDROID)
181  loadArray loads;
182  if (getloadavg(loads.data(), loads.size()) != -1)
183  return loads;
184 #endif
185  return {-1, -1, -1};
186 }
187 
195 bool hasUtf8(const char *str)
196 {
197  const uchar *c = (uchar *) str;
198 
199  while (*c++)
200  {
201  // ASCII is < 0x80.
202  // 0xC2..0xF4 is probably UTF-8.
203  // Anything else probably ISO-8859-1 (Latin-1, Unicode)
204 
205  if (*c > 0xC1 && *c < 0xF5)
206  {
207  int bytesToCheck = 2; // Assume 0xC2-0xDF (2 byte sequence)
208 
209  if (*c > 0xDF) // Maybe 0xE0-0xEF (3 byte sequence)
210  ++bytesToCheck;
211  if (*c > 0xEF) // Matches 0xF0-0xF4 (4 byte sequence)
212  ++bytesToCheck;
213 
214  while (bytesToCheck--)
215  {
216  ++c;
217 
218  if (! *c) // String ended in middle
219  return false; // Not valid UTF-8
220 
221  if (*c < 0x80 || *c > 0xBF) // Bad UTF-8 sequence
222  break; // Keep checking in outer loop
223  }
224 
225  if (!bytesToCheck) // Have checked all the bytes in the sequence
226  return true; // Hooray! We found valid UTF-8!
227  }
228  }
229 
230  return false;
231 }
232 
247 bool ping(const QString &host, int timeout)
248 {
249 #ifdef _WIN32
250  QString cmd = QString("%systemroot%\\system32\\ping.exe -w %1 -n 1 %2>NUL")
251  .arg(timeout*1000).arg(host);
252 
255 #else
256  QString addrstr =
258  QHostAddress addr = QHostAddress(addrstr);
259 #if defined(__FreeBSD__) || CONFIG_DARWIN
260  QString timeoutparam("-t");
261 #else
262  // Linux, NetBSD, OpenBSD
263  QString timeoutparam("-w");
264 #endif
265  QString pingcmd =
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);
269 
272 #endif
273 }
274 
278 bool telnet(const QString &host, int port)
279 {
280  auto *s = new MythSocket();
281 
282  bool connected = s->ConnectToHost(host, port);
283  s->DecrRef();
284 
285  return connected;
286 }
287 
309 long long copy(QFile &dst, QFile &src, uint block_size)
310 {
311  uint buflen = (block_size < 1024) ? (16 * 1024) : block_size;
312  char *buf = new char[buflen];
313  bool odst = false;
314  bool osrc = false;
315 
316  if (!buf)
317  return -1LL;
318 
319  if (!dst.isWritable() && !dst.isOpen())
320  {
321  odst = dst.open(QIODevice::Unbuffered |
322  QIODevice::WriteOnly |
323  QIODevice::Truncate);
324  }
325 
326  if (!src.isReadable() && !src.isOpen())
327  osrc = src.open(QIODevice::Unbuffered|QIODevice::ReadOnly);
328 
329  bool ok = dst.isWritable() && src.isReadable();
330  long long total_bytes = 0LL;
331  while (ok)
332  {
333  long long off = 0;
334  long long rlen = src.read(buf, buflen);
335  if (rlen<0)
336  {
337  LOG(VB_GENERAL, LOG_ERR, "read error");
338  ok = false;
339  break;
340  }
341  if (rlen==0)
342  break;
343 
344  total_bytes += rlen;
345 
346  while ((rlen-off>0) && ok)
347  {
348  long long wlen = dst.write(buf + off, rlen - off);
349  if (wlen>=0)
350  off+= wlen;
351  if (wlen<0)
352  {
353  LOG(VB_GENERAL, LOG_ERR, "write error");
354  ok = false;
355  }
356  }
357  }
358  delete[] buf;
359 
360  if (odst)
361  dst.close();
362 
363  if (osrc)
364  src.close();
365 
366  return (ok) ? total_bytes : -1LL;
367 }
368 
369 QString createTempFile(QString name_template, bool dir)
370 {
371  int ret = -1;
372 
373 #ifdef _WIN32
374  char temppath[MAX_PATH] = ".";
375  char tempfilename[MAX_PATH] = "";
376  // if GetTempPath fails, use current dir
377  GetTempPathA(MAX_PATH, temppath);
378  if (GetTempFileNameA(temppath, "mth", 0, tempfilename))
379  {
380  if (dir)
381  {
382  // GetTempFileNameA creates the file, so delete it before mkdir
383  unlink(tempfilename);
384  ret = mkdir(tempfilename);
385  }
386  else
387  ret = open(tempfilename, O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
388  }
389  QString tmpFileName(tempfilename);
390 #else
391  QByteArray safe_name_template = name_template.toLatin1();
392  const char *tmp = safe_name_template.constData();
393  char *ctemplate = strdup(tmp);
394 
395  if (dir)
396  {
397  ret = (mkdtemp(ctemplate)) ? 0 : -1;
398  }
399  else
400  {
401  mode_t cur_umask = umask(S_IRWXO | S_IRWXG);
402  ret = mkstemp(ctemplate);
403  umask(cur_umask);
404  }
405 
406  QString tmpFileName(ctemplate);
407  free(ctemplate);
408 #endif
409 
410  if (ret == -1)
411  {
412  LOG(VB_GENERAL, LOG_ERR, QString("createTempFile(%1), Error ")
413  .arg(name_template) + ENO);
414  return name_template;
415  }
416 
417  if (!dir && (ret >= 0))
418  close(ret);
419 
420  return tmpFileName;
421 }
422 
441 bool makeFileAccessible(const QString& filename)
442 {
443  QByteArray fname = filename.toLatin1();
444  int ret = chmod(fname.constData(), 0666);
445  if (ret == -1)
446  {
447  LOG(VB_GENERAL, LOG_ERR, QString("Unable to change permissions on file. (%1)").arg(filename));
448  return false;
449  }
450  return true;
451 }
452 
456 QString getResponse(const QString &query, const QString &def)
457 {
458  QByteArray tmp = query.toLocal8Bit();
459  std::cout << tmp.constData();
460 
461  tmp = def.toLocal8Bit();
462  if (!def.isEmpty())
463  std::cout << " [" << tmp.constData() << "] ";
464  else
465  std::cout << " ";
466 
467  if (!isatty(fileno(stdin)) || !isatty(fileno(stdout)))
468  {
469  std::cout << std::endl << "[console is not interactive, using default '"
470  << tmp.constData() << "']" << std::endl;
471  return def;
472  }
473 
474  QTextStream stream(stdin);
475  QString qresponse = stream.readLine();
476 
477  if (qresponse.isEmpty())
478  qresponse = def;
479 
480  return qresponse;
481 }
482 
486 int intResponse(const QString &query, int def)
487 {
488  QString str_resp = getResponse(query, QString("%1").arg(def));
489  if (str_resp.isEmpty())
490  return def;
491  bool ok = false;
492  int resp = str_resp.toInt(&ok);
493  return (ok ? resp : def);
494 }
495 
496 
497 QString getSymlinkTarget(const QString &start_file,
498  QStringList *intermediaries,
499  unsigned maxLinks)
500 {
501 #if 0
502  LOG(VB_GENERAL, LOG_DEBUG,
503  QString("getSymlinkTarget('%1', 0x%2, %3)")
504  .arg(start_file).arg((uint64_t)intermediaries,0,16)
505  .arg(maxLinks));
506 #endif
507 
508  QString link;
509  QString cur_file = start_file;
510  QFileInfo fi(cur_file);
511 
512  if (intermediaries)
513  {
514  intermediaries->clear();
515  intermediaries->push_back(start_file);
516  }
517 
518  for (uint i = 0; (i <= maxLinks) && fi.isSymLink() &&
519  !(link = fi.symLinkTarget()).isEmpty(); i++)
520  {
521  cur_file = (link[0] == '/') ?
522  link : // absolute link
523  fi.absoluteDir().absolutePath() + "/" + link; // relative link
524 
525  if (intermediaries && !intermediaries->contains(cur_file))
526  intermediaries->push_back(cur_file);
527 
528  fi = QFileInfo(cur_file);
529  }
530 
531 #if 0
532  if (intermediaries)
533  {
534  for (uint i = 0; i < intermediaries->size(); i++)
535  {
536  LOG(VB_GENERAL, LOG_DEBUG, QString(" inter%1: %2")
537  .arg(i).arg((*intermediaries)[i]));
538  }
539  }
540 
541  LOG(VB_GENERAL, LOG_DEBUG,
542  QString("getSymlinkTarget() -> '%1'")
543  .arg((!fi.isSymLink()) ? cur_file : QString()));
544 #endif
545 
546  return (!fi.isSymLink()) ? cur_file : QString();
547 }
548 
549 bool IsMACAddress(const QString& MAC)
550 {
551  QStringList tokens = MAC.split(':');
552  if (tokens.size() != 6)
553  {
554  LOG(VB_NETWORK, LOG_ERR,
555  QString("IsMACAddress(%1) = false, doesn't have 6 parts").arg(MAC));
556  return false;
557  }
558 
559  for (int y = 0; y < 6; y++)
560  {
561  if (tokens[y].isEmpty())
562  {
563  LOG(VB_NETWORK, LOG_ERR,
564  QString("IsMACAddress(%1) = false, part #%2 is empty.")
565  .arg(MAC).arg(y));
566  return false;
567  }
568 
569  bool ok = false;
570  int value = tokens[y].toInt(&ok, 16);
571  if (!ok)
572  {
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]));
577  return false;
578  }
579 
580  if (value > 255)
581  {
582  LOG(VB_NETWORK, LOG_ERR,
583  QString("IsMACAddress(%1) = false, part #%2 "
584  "evaluates to %3 which is higher than 255.")
585  .arg(MAC).arg(y).arg(value));
586  return false;
587  }
588  }
589 
590  LOG(VB_NETWORK, LOG_DEBUG, QString("IsMACAddress(%1) = true").arg(MAC));
591  return true;
592 }
593 
594 QString FileHash(const QString& filename)
595 {
596  QFile file(filename);
597  QFileInfo fileinfo(file);
598  qint64 initialsize = fileinfo.size();
599  quint64 hash = 0;
600 
601  if (initialsize == 0)
602  return QString("NULL");
603 
604  if (file.open(QIODevice::ReadOnly))
605  hash = initialsize;
606  else
607  {
608  LOG(VB_GENERAL, LOG_ERR,
609  "Error: Unable to open selected file, missing read permissions?");
610  return QString("NULL");
611  }
612 
613  file.seek(0);
614  QDataStream stream(&file);
615  stream.setByteOrder(QDataStream::LittleEndian);
616  for (quint64 tmp = 0, i = 0; i < 65536/sizeof(tmp); i++)
617  {
618  stream >> tmp;
619  hash += tmp;
620  }
621 
622  file.seek(initialsize - 65536);
623  for (quint64 tmp = 0, i = 0; i < 65536/sizeof(tmp); i++)
624  {
625  stream >> tmp;
626  hash += tmp;
627  }
628 
629  file.close();
630 
631  QString output = QString("%1").arg(hash, 0, 16);
632  return output;
633 }
634 
635 bool WakeOnLAN(const QString& MAC)
636 {
637  std::vector<char> msg(6, static_cast<char>(0xFF));
638  std::array<char,6> macaddr {};
639  QStringList tokens = MAC.split(':');
640 
641  if (tokens.size() != 6)
642  {
643  LOG(VB_GENERAL, LOG_ERR,
644  QString( "WakeOnLan(%1): Incorrect MAC length").arg(MAC));
645  return false;
646  }
647 
648  for (int y = 0; y < 6; y++)
649  {
650  bool ok = false;
651  macaddr[y] = tokens[y].toInt(&ok, 16);
652 
653  if (!ok)
654  {
655  LOG(VB_GENERAL, LOG_ERR,
656  QString( "WakeOnLan(%1): Invalid MAC address").arg(MAC));
657  return false;
658  }
659  }
660 
661  msg.reserve(1024);
662  for (int x = 0; x < 16; x++)
663  msg.insert(msg.end(), macaddr.cbegin(), macaddr.cend());
664 
665  LOG(VB_NETWORK, LOG_INFO,
666  QString("WakeOnLan(): Sending WOL packet to %1").arg(MAC));
667 
668  QUdpSocket udp_socket;
669  qlonglong msglen = msg.size();
670  return udp_socket.writeDatagram(
671  msg.data(), msglen, QHostAddress::Broadcast, 32767) == msglen;
672 }
673 
674 // Wake up either by command or by MAC address
675 // return true = success
676 bool MythWakeup(const QString &wakeUpCommand, uint flags, uint timeout)
677 {
678  if (!IsMACAddress(wakeUpCommand))
679  return myth_system(wakeUpCommand, flags, timeout) == 0U;
680 
681  return WakeOnLAN(wakeUpCommand);
682 }
683 
685 {
686 #ifdef _WIN32
687  return false;
688 #else
689 
690 #if CONFIG_DARWIN || (__FreeBSD__) || defined(__OpenBSD__)
691  const char *command = "ps -ax | grep -i pulseaudio | grep -v grep > /dev/null";
692 #else
693  const char *command = "ps ch -C pulseaudio -o pid > /dev/null";
694 #endif
695  // Do NOT use kMSProcessEvents here, it will cause deadlock
696  uint res = myth_system(command, kMSDontBlockInputDevs |
698  return (res == GENERIC_EXIT_OK);
699 #endif // _WIN32
700 }
701 
702 bool myth_nice(int val)
703 {
704  errno = 0;
705  int ret = nice(val);
706 
707  if ((-1 == ret) && (0 != errno) && (val >= 0))
708  {
709  LOG(VB_GENERAL, LOG_ERR, "Failed to nice process" + ENO);
710  return false;
711  }
712 
713  return true;
714 }
715 
716 void myth_yield(void)
717 {
718 #ifdef _POSIX_PRIORITY_SCHEDULING
719  if (sched_yield()<0)
720  usleep(5000);
721 #else
722  usleep(5000);
723 #endif
724 }
725 
743 #if defined(__linux__) && ( defined(__i386__) || defined(__ppc__) || \
744  defined(__x86_64__) || defined(__ia64__) )
745 
746 #include <cstdio>
747 #include <cstdlib>
748 #include <cerrno>
749 #include <getopt.h>
750 #include <unistd.h>
751 #include <sys/ptrace.h>
752 #include <asm/unistd.h>
753 
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
766 #endif
767 
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))
774 
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, };
777 
778 bool myth_ioprio(int val)
779 {
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);
784 
785  int pid = getpid();
786  int old_ioprio = syscall(NR_ioprio_get, IOPRIO_WHO_PROCESS, pid);
787  if (old_ioprio == new_ioprio)
788  return true;
789 
790  int ret = syscall(NR_ioprio_set, IOPRIO_WHO_PROCESS, pid, new_ioprio);
791 
792  if (-1 == ret && EPERM == errno && IOPRIO_CLASS_BE != new_ioclass)
793  {
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);
797  }
798 
799  return 0 == ret;
800 }
801 
802 #else
803 
804 bool myth_ioprio(int) { return true; }
805 
806 #endif
807 
808 bool MythRemoveDirectory(QDir &aDir)
809 {
810  if (!aDir.exists())//QDir::NoDotAndDotDot
811  return false;
812 
813  QFileInfoList entries = aDir.entryInfoList(QDir::NoDotAndDotDot |
814  QDir::Dirs | QDir::Files);
815  int count = entries.size();
816  bool has_err = false;
817 
818  for (int idx = 0; idx < count && !has_err; idx++)
819  {
820  QFileInfo entryInfo = entries[idx];
821  QString path = entryInfo.absoluteFilePath();
822  if (entryInfo.isDir())
823  {
824  QDir dir(path);
825  has_err = MythRemoveDirectory(dir);
826  }
827  else
828  {
829  QFile file(path);
830  if (!file.remove())
831  has_err = true;
832  }
833  }
834 
835  if (!has_err && !aDir.rmdir(aDir.absolutePath()))
836  has_err = true;
837 
838  return(has_err);
839 }
840 
852 void setHttpProxy(void)
853 {
854  QString LOC = "setHttpProxy() - ";
855 
856  // Set http proxy for the application if specified in environment variable
857  QString var(qEnvironmentVariable("http_proxy"));
858  if (var.isEmpty())
859  var = qEnvironmentVariable("HTTP_PROXY"); // Sadly, some OS envs are case sensitive
860  if (!var.isEmpty())
861  {
862  if (!var.startsWith("http://")) // i.e. just a host name
863  var.prepend("http://");
864 
865  QUrl url = QUrl(var, QUrl::TolerantMode);
866  QString host = url.host();
867  int port = url.port();
868 
869  if (port == -1) // Parsing error
870  {
871  port = 0; // The default when creating a QNetworkProxy
872 
873  if (telnet(host, 1080)) // Socks?
874  port = 1080;
875  if (telnet(host, 3128)) // Squid
876  port = 3128;
877  if (telnet(host, 8080)) // MS ISA
878  port = 8080;
879 
880  LOG(VB_NETWORK, LOG_INFO, LOC +
881  QString("assuming port %1 on host %2") .arg(port).arg(host));
882  url.setPort(port);
883  }
884  else if (!ping(host, 1))
885  {
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!");
889  }
890  else if (!telnet(host,port))
891  {
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!");
895  }
896 
897 #if 0
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));
901 #endif
902  QNetworkProxy p =
903  QNetworkProxy(QNetworkProxy::HttpCachingProxy,
904  host, port, url.userName(), url.password());
905  QNetworkProxy::setApplicationProxy(p);
906  return;
907  }
908 
909  LOG(VB_NETWORK, LOG_DEBUG, LOC + "no HTTP_PROXY environment var.");
910 
911  // Use Qt to look for user proxy settings stored by the OS or browser:
912 
913  QList<QNetworkProxy> proxies;
914  QNetworkProxyQuery query(QUrl("http://www.mythtv.org"));
915 
916  proxies = QNetworkProxyFactory::systemProxyForQuery(query);
917 
918  for (const auto& p : qAsConst(proxies))
919  {
920  QString host = p.hostName();
921  int port = p.port();
922 
923  if (p.type() == QNetworkProxy::NoProxy)
924  continue;
925 
926  if (!telnet(host, port))
927  {
928  LOG(VB_NETWORK, LOG_ERR, LOC +
929  "failed to contact proxy host " + host);
930  continue;
931  }
932 
933  LOG(VB_NETWORK, LOG_INFO, LOC + QString("using proxy host %1:%2")
934  .arg(host).arg(port));
935  QNetworkProxy::setApplicationProxy(p);
936 
937  // Allow sub-commands to use this proxy
938  // via myth_system(command), by setting HTTP_PROXY
939  QString url;
940 
941  if (!p.user().isEmpty())
942  {
943  url = "http://%1:%2@%3:%4",
944  url = url.arg(p.user()).arg(p.password());
945  }
946  else
947  {
948  url = "http://%1:%2";
949  }
950 
951  url = url.arg(p.hostName()).arg(p.port());
952  setenv("HTTP_PROXY", url.toLatin1(), 1);
953  setenv("http_proxy", url.toLatin1(), 0);
954 
955  return;
956  }
957 
958  LOG(VB_NETWORK, LOG_ERR, LOC + "failed to find a network proxy");
959 }
960 
961 void wrapList(QStringList &list, int width)
962 {
963  // if this is triggered, something has gone seriously wrong
964  // the result won't really be usable, but at least it won't crash
965  width = std::max(width, 5);
966 
967  for (int i = 0; i < list.size(); i++)
968  {
969  QString string = list.at(i);
970 
971  if( string.size() <= width )
972  continue;
973 
974  QString left = string.left(width);
975  bool inserted = false;
976 
977  while( !inserted && !left.endsWith(" " ))
978  {
979  if( string.mid(left.size(), 1) == " " )
980  {
981  list.replace(i, left);
982  list.insert(i+1, string.mid(left.size()).trimmed());
983  inserted = true;
984  }
985  else
986  {
987  left.chop(1);
988  if( !left.contains(" ") )
989  {
990  // Line is too long, just hyphenate it
991  list.replace(i, left + "-");
992  list.insert(i+1, string.mid(left.size()));
993  inserted = true;
994  }
995  }
996  }
997 
998  if( !inserted )
999  {
1000  left.chop(1);
1001  list.replace(i, left);
1002  list.insert(i+1, string.mid(left.size()).trimmed());
1003  }
1004  }
1005 }
1006 
1007 QString xml_indent(uint level)
1008 {
1009  static QReadWriteLock s_rwLock;
1010  static QMap<uint,QString> s_cache;
1011 
1012  s_rwLock.lockForRead();
1013  QMap<uint,QString>::const_iterator it = s_cache.constFind(level);
1014  if (it != s_cache.constEnd())
1015  {
1016  QString tmp = *it;
1017  s_rwLock.unlock();
1018  return tmp;
1019  }
1020  s_rwLock.unlock();
1021 
1022  QString ret = "";
1023  for (uint i = 0; i < level; i++)
1024  ret += " ";
1025 
1026  s_rwLock.lockForWrite();
1027  s_cache[level] = ret;
1028  s_rwLock.unlock();
1029 
1030  return ret;
1031 }
1032 
1033 int naturalCompare(const QString &_a, const QString &_b, Qt::CaseSensitivity caseSensitivity)
1034 {
1035  // This method chops the input a and b into pieces of
1036  // digits and non-digits (a1.05 becomes a | 1 | . | 05)
1037  // and compares these pieces of a and b to each other
1038  // (first with first, second with second, ...).
1039  //
1040  // This is based on the natural sort order code code by Martin Pool
1041  // http://sourcefrog.net/projects/natsort/
1042  // Martin Pool agreed to license this under LGPL or GPL.
1043 
1044  // FIXME: Using toLower() to implement case insensitive comparison is
1045  // sub-optimal, but is needed because we compare strings with
1046  // localeAwareCompare(), which does not know about case sensitivity.
1047  // A task has been filled for this in Qt Task Tracker with ID 205990.
1048  // http://trolltech.com/developer/task-tracker/index_html?method=entry&id=205990
1049 
1050  QString a;
1051  QString b;
1052 
1053  if (caseSensitivity == Qt::CaseSensitive)
1054  {
1055  a = _a;
1056  b = _b;
1057  }
1058  else
1059  {
1060  a = _a.toLower();
1061  b = _b.toLower();
1062  }
1063 
1064  const QChar* currA = a.unicode(); // iterator over a
1065  const QChar* currB = b.unicode(); // iterator over b
1066 
1067  if (currA == currB)
1068  {
1069  return 0;
1070  }
1071 
1072  while (!currA->isNull() && !currB->isNull())
1073  {
1074  const QChar* begSeqA = currA; // beginning of a new character sequence of a
1075  const QChar* begSeqB = currB;
1076 
1077  if (currA->unicode() == QChar::ObjectReplacementCharacter)
1078  {
1079  return 1;
1080  }
1081 
1082  if (currB->unicode() == QChar::ObjectReplacementCharacter)
1083  {
1084  return -1;
1085  }
1086 
1087  if (currA->unicode() == QChar::ReplacementCharacter)
1088  {
1089  return 1;
1090  }
1091 
1092  if (currB->unicode() == QChar::ReplacementCharacter)
1093  {
1094  return -1;
1095  }
1096 
1097  // find sequence of characters ending at the first non-character
1098  while (!currA->isNull() && !currA->isDigit() && !currA->isPunct() &&
1099  !currA->isSpace())
1100  {
1101  ++currA;
1102  }
1103 
1104  while (!currB->isNull() && !currB->isDigit() && !currB->isPunct() &&
1105  !currB->isSpace())
1106  {
1107  ++currB;
1108  }
1109 
1110  // compare these sequences
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);
1114 
1115  if (cmp != 0)
1116  {
1117  return cmp < 0 ? -1 : +1;
1118  }
1119 
1120  if (currA->isNull() || currB->isNull())
1121  {
1122  break;
1123  }
1124 
1125  // find sequence of characters ending at the first non-character
1126  while ((currA->isPunct() || currA->isSpace()) &&
1127  (currB->isPunct() || currB->isSpace()))
1128  {
1129  if (*currA != *currB)
1130  {
1131  return (*currA < *currB) ? -1 : +1;
1132  }
1133  ++currA;
1134  ++currB;
1135  if (currA->isNull() || currB->isNull())
1136  {
1137  break;
1138  }
1139  }
1140 
1141  // now some digits follow...
1142  if ((*currA == QLatin1Char('0')) || (*currB == QLatin1Char('0')))
1143  {
1144  // one digit-sequence starts with 0 -> assume we are in a fraction part
1145  // do left aligned comparison (numbers are considered left aligned)
1146  while (true)
1147  {
1148  if (!currA->isDigit() && !currB->isDigit())
1149  {
1150  break;
1151  }
1152  if (!currA->isDigit())
1153  {
1154  return +1;
1155  }
1156  if (!currB->isDigit())
1157  {
1158  return -1;
1159  }
1160  if (*currA < *currB)
1161  {
1162  return -1;
1163  }
1164  if (*currA > *currB)
1165  {
1166  return + 1;
1167  }
1168  ++currA;
1169  ++currB;
1170  }
1171  }
1172  else
1173  {
1174  // No digit-sequence starts with 0 -> assume we are looking at some integer
1175  // do right aligned comparison.
1176  //
1177  // The longest run of digits wins. That aside, the greatest
1178  // value wins, but we can't know that it will until we've scanned
1179  // both numbers to know that they have the same magnitude.
1180 
1181  bool isFirstRun = true;
1182  int weight = 0;
1183 
1184  while (true)
1185  {
1186  if (!currA->isDigit() && !currB->isDigit())
1187  {
1188  if (weight != 0)
1189  {
1190  return weight;
1191  }
1192  break;
1193  }
1194  if (!currA->isDigit())
1195  {
1196  if (isFirstRun)
1197  {
1198  return *currA < *currB ? -1 : +1;
1199  }
1200  return -1;
1201  }
1202  if (!currB->isDigit())
1203  {
1204  if (isFirstRun)
1205  {
1206  return *currA < *currB ? -1 : +1;
1207  }
1208  return +1;
1209  }
1210  if ((*currA < *currB) && (weight == 0))
1211  {
1212  weight = -1;
1213  }
1214  else if ((*currA > *currB) && (weight == 0))
1215  {
1216  weight = + 1;
1217  }
1218  ++currA;
1219  ++currB;
1220  isFirstRun = false;
1221  }
1222  }
1223  }
1224 
1225  if (currA->isNull() && currB->isNull())
1226  {
1227  return 0;
1228  }
1229 
1230  return currA->isNull() ? -1 : + 1;
1231 }
1232 
1233 QString MythFormatTimeMs(int msecs, const QString& fmt)
1234 {
1235  return QTime::fromMSecsSinceStartOfDay(msecs).toString(fmt);
1236 }
1237 
1238 QString MythFormatTime(int secs, const QString& fmt)
1239 {
1240  return QTime::fromMSecsSinceStartOfDay(secs*1000).toString(fmt);
1241 }
1242 
1243 /*
1244  * States for the command line parser.
1245  */
1246 enum states {
1247  START, // No current token.
1248  INTEXT, // Collecting token text.
1249  INSQUOTE, // Collecting token, inside single quotes.
1250  INDQUOTE, // Collecting token, inside double quotes.
1251  ESCTEXT, // Saw backslash. Returns to generic text.
1252  ESCSQUOTE, // Saw backslash. Returns to single quotes.
1253  ESCDQUOTE, // Saw backslash. Returns to double quotes.
1254 };
1255 
1256 /*
1257  * Parse a string into separate tokens. This function understands
1258  * quoting and the escape character.
1259  */
1260 QStringList MythSplitCommandString(const QString &line)
1261 {
1262  QStringList fields;
1263  states state = START;
1264  int tokenStart = -1;
1265 
1266  for (int i = 0; i < line.size(); i++)
1267  {
1268  const QChar c = line.at(i);
1269 
1270  switch (state) {
1271  case START:
1272  tokenStart = i;
1273  if (c.isSpace()) break;
1274  if (c == '\'') state = INSQUOTE;
1275  else if (c == '\"') state = INDQUOTE;
1276  else if (c == '\\') state = ESCTEXT;
1277  else state = INTEXT;
1278  break;
1279  case INTEXT:
1280  if (c.isSpace()) {
1281  fields += line.mid(tokenStart, i - tokenStart);
1282  state = START;
1283  break;
1284  }
1285  else if (c == '\'') state = INSQUOTE;
1286  else if (c == '\"') state = INDQUOTE;
1287  else if (c == '\\') state = ESCTEXT;
1288  break;
1289  case INSQUOTE:
1290  if (c == '\'') state = INTEXT;
1291  else if (c == '\\') state = ESCSQUOTE;
1292  break;
1293  case INDQUOTE:
1294  if (c == '\"') state = INTEXT;
1295  else if (c == '\\') state = ESCDQUOTE;
1296  break;
1297  case ESCTEXT: state = INTEXT; break;
1298  case ESCSQUOTE: state = INSQUOTE; break;
1299  case ESCDQUOTE: state = INDQUOTE; break;
1300  }
1301  }
1302 
1303  if (state != START)
1304  fields += line.mid(tokenStart);
1305  return fields;
1306 }
1307 
1308 /* vim: set expandtab tabstop=4 shiftwidth=4: */
MythFormatTimeMs
QString MythFormatTimeMs(int msecs, const QString &fmt)
Format a milliseconds time value.
Definition: mythmiscutil.cpp:1233
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:72
myth_system
uint myth_system(const QString &command, uint flags, uint timeout)
Definition: mythsystemlegacy.cpp:501
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:103
GENERIC_EXIT_OK
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
setHttpProxy
void setHttpProxy(void)
Get network proxy settings from OS, and use for [Q]Http[Comms].
Definition: mythmiscutil.cpp:852
copy
long long copy(QFile &dst, QFile &src, uint block_size)
Copies src file to dst file.
Definition: mythmiscutil.cpp:309
kMSDontBlockInputDevs
@ kMSDontBlockInputDevs
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
getDiskSpace
int64_t getDiskSpace(const QString &file_on_disk, int64_t &total, int64_t &used)
Definition: mythcoreutil.cpp:43
myth_ioprio
bool myth_ioprio(int)
Allows setting the I/O priority of the current process/thread.
Definition: mythmiscutil.cpp:804
mythcoreutil.h
getLoadAvgs
loadArray getLoadAvgs(void)
Returns the system load averages.
Definition: mythmiscutil.cpp:178
LOC
#define LOC
Definition: audioconvert.cpp:38
setenv
#define setenv(x, y, z)
Definition: compat.h:158
arg
arg(title).arg(filename).arg(doDelete))
getSymlinkTarget
QString getSymlinkTarget(const QString &start_file, QStringList *intermediaries, unsigned maxLinks)
Definition: mythmiscutil.cpp:497
WakeOnLAN
bool WakeOnLAN(const QString &MAC)
Definition: mythmiscutil.cpp:635
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
build_compdb.file
file
Definition: build_compdb.py:55
telnet
bool telnet(const QString &host, int port)
Can we talk to port on host?
Definition: mythmiscutil.cpp:278
ESCDQUOTE
@ ESCDQUOTE
Definition: mythmiscutil.cpp:1253
getloadavg
#define getloadavg(x, y)
Definition: compat.h:324
close
#define close
Definition: compat.h:17
tmp
static guint32 * tmp
Definition: goom_core.cpp:31
mythsystemlegacy.h
IsPulseAudioRunning
bool IsPulseAudioRunning(void)
Is A/V Sync destruction daemon is running on this host?
Definition: mythmiscutil.cpp:684
MythSocket
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:26
MythRemoveDirectory
bool MythRemoveDirectory(QDir &aDir)
Definition: mythmiscutil.cpp:808
MythWakeup
bool MythWakeup(const QString &wakeUpCommand, uint flags, uint timeout)
Definition: mythmiscutil.cpp:676
wrapList
void wrapList(QStringList &list, int width)
Definition: mythmiscutil.cpp:961
INSQUOTE
@ INSQUOTE
Definition: mythmiscutil.cpp:1249
mythlogging.h
hardwareprofile.config.p
p
Definition: config.py:33
compat.h
MythFormatTime
QString MythFormatTime(int secs, const QString &fmt)
Format a seconds time value.
Definition: mythmiscutil.cpp:1238
START
@ START
Definition: mythmiscutil.cpp:1247
createTempFile
QString createTempFile(QString name_template, bool dir)
Definition: mythmiscutil.cpp:369
getResponse
QString getResponse(const QString &query, const QString &def)
In an interactive shell, prompt the user to input a string.
Definition: mythmiscutil.cpp:456
filename
QString filename
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:637
loadArray
std::array< double, 3 > loadArray
Definition: mythmiscutil.h:36
hardwareprofile.distros.mythtv_data.main.stdout
stdout
Definition: main.py:87
naturalCompare
int naturalCompare(const QString &_a, const QString &_b, Qt::CaseSensitivity caseSensitivity)
Definition: mythmiscutil.cpp:1033
uint
unsigned int uint
Definition: compat.h:141
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
getUptime
bool getUptime(time_t &uptime)
Returns uptime statistics.
Definition: mythmiscutil.cpp:68
states
states
Definition: mythmiscutil.cpp:1246
MythCoreContext::resolveAddress
static QString resolveAddress(const QString &host, ResolveType type=ResolveAny, bool keepscope=false)
if host is an IP address, it will be returned or resolved otherwise.
Definition: mythcorecontext.cpp:1201
block_size
unsigned int block_size
Definition: freesurround.cpp:47
ESCSQUOTE
@ ESCSQUOTE
Definition: mythmiscutil.cpp:1252
hasUtf8
bool hasUtf8(const char *str)
Guess whether a string is UTF-8.
Definition: mythmiscutil.cpp:195
INDQUOTE
@ INDQUOTE
Definition: mythmiscutil.cpp:1250
mythmiscutil.h
makeFileAccessible
bool makeFileAccessible(const QString &filename)
Definition: mythmiscutil.cpp:441
mythcorecontext.h
dir
QDir dir
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1174
intResponse
int intResponse(const QString &query, int def)
In an interactive shell, prompt the user to input a number.
Definition: mythmiscutil.cpp:486
kMSProcessEvents
@ kMSProcessEvents
process events while waiting
Definition: mythsystem.h:37
xml_indent
QString xml_indent(uint level)
Definition: mythmiscutil.cpp:1007
INTEXT
@ INTEXT
Definition: mythmiscutil.cpp:1248
nice
#define nice(x)
Definition: compat.h:197
getMemStats
bool getMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
Returns memory statistics in megabytes.
Definition: mythmiscutil.cpp:111
myth_nice
bool myth_nice(int val)
Definition: mythmiscutil.cpp:702
MythSplitCommandString
QStringList MythSplitCommandString(const QString &line)
Definition: mythmiscutil.cpp:1260
mythburn.temppath
string temppath
Definition: mythburn.py:161
kMSDontDisableDrawing
@ kMSDontDisableDrawing
avoid disabling UI drawing
Definition: mythsystem.h:35
exitcodes.h
FileHash
QString FileHash(const QString &filename)
Definition: mythmiscutil.cpp:594
IsMACAddress
bool IsMACAddress(const QString &MAC)
Definition: mythmiscutil.cpp:549
mythsocket.h
hardwareprofile.smolt.proxies
proxies
Definition: smolt.py:104
query
MSqlQuery query(MSqlQuery::InitCon())
output
#define output
Definition: synaesthesia.cpp:220
ESCTEXT
@ ESCTEXT
Definition: mythmiscutil.cpp:1251
MythCoreContext::ResolveAny
@ ResolveAny
Definition: mythcorecontext.h:201
myth_yield
void myth_yield(void)
Definition: mythmiscutil.cpp:716
ping
bool ping(const QString &host, int timeout)
Can we ping host within timeout seconds?
Definition: mythmiscutil.cpp:247