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