| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 3 | * 2013-2022 by the Falcon Player Developers. | ||
| 4 | * | ||
| 5 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 6 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 7 | * file for descriptions of what files are covered by each license. | ||
| 8 | * | ||
| 9 | * This source file is covered under the LGPL v2.1 as described in the | ||
| 10 | * included LICENSE.LGPL file. | ||
| 11 | */ | ||
| 12 | // This #define must be before any #include's | ||
| 13 | #define _FILE_OFFSET_BITS 64 | ||
| 14 | #define __STDC_FORMAT_MACROS | ||
| 15 | |||
| 16 | #include "common_mini.h" | ||
| 17 | #include "SysSocket.h" | ||
| 18 | |||
| 19 | #ifndef _GNU_SOURCE | ||
| 20 | #define _GNU_SOURCE | ||
| 21 | #endif | ||
| 22 | |||
| 23 | #if defined(__APPLE__) | ||
| 24 | #include <copyfile.h> | ||
| 25 | #else | ||
| 26 | #include <sys/sendfile.h> | ||
| 27 | #endif | ||
| 28 | |||
| 29 | #include <arpa/inet.h> | ||
| 30 | #include <curl/curl.h> | ||
| 31 | #include <net/if.h> | ||
| 32 | #include <netinet/in.h> | ||
| 33 | #include <sys/file.h> | ||
| 34 | #include <sys/ioctl.h> | ||
| 35 | #include <sys/socket.h> | ||
| 36 | #include <sys/stat.h> | ||
| 37 | #include <sys/time.h> | ||
| 38 | #include <algorithm> | ||
| 39 | #include <climits> | ||
| 40 | #include <cstdint> | ||
| 41 | #include <ctime> | ||
| 42 | #include <ctype.h> | ||
| 43 | #include <cxxabi.h> | ||
| 44 | #include <dirent.h> | ||
| 45 | #include <errno.h> | ||
| 46 | #include <fcntl.h> | ||
| 47 | #include <ifaddrs.h> | ||
| 48 | #include <iomanip> | ||
| 49 | #include <list> | ||
| 50 | #include <map> | ||
| 51 | #include <netdb.h> | ||
| 52 | #include <pwd.h> | ||
| 53 | #include <random> | ||
| 54 | #include <sstream> | ||
| 55 | #include <stdio.h> | ||
| 56 | #include <stdlib.h> | ||
| 57 | #include <string.h> | ||
| 58 | #include <string> | ||
| 59 | #include <unistd.h> | ||
| 60 | #include <utility> | ||
| 61 | #include <vector> | ||
| 62 | |||
| 63 | /* | ||
| 64 | * Get the current time down to the microsecond | ||
| 65 | */ | ||
| 66 | 34173 | long long GetTime(void) { | |
| 67 | struct timeval now_tv; | ||
| 68 | 34173 | gettimeofday(&now_tv, NULL); | |
| 69 | 34173 | return now_tv.tv_sec * 1000000LL + now_tv.tv_usec; | |
| 70 | } | ||
| 71 | 44 | long long GetTimeMicros(void) { | |
| 72 | struct timeval now_tv; | ||
| 73 | 44 | gettimeofday(&now_tv, NULL); | |
| 74 | 44 | return now_tv.tv_sec * 1000000LL + now_tv.tv_usec; | |
| 75 | } | ||
| 76 | |||
| 77 | 466998 | long long GetTimeMS(void) { | |
| 78 | struct timeval now_tv; | ||
| 79 | 466998 | gettimeofday(&now_tv, NULL); | |
| 80 | 466998 | return now_tv.tv_sec * 1000LL + now_tv.tv_usec / 1000; | |
| 81 | } | ||
| 82 | |||
| 83 | 2656751 | long long GetMonotonicTimeMS(void) { | |
| 84 | struct timespec ts; | ||
| 85 | 2656751 | clock_gettime(CLOCK_MONOTONIC, &ts); | |
| 86 | 2656751 | return ts.tv_sec * 1000LL + ts.tv_nsec / 1000000; | |
| 87 | } | ||
| 88 | |||
| 89 | 969196 | long long GetMonotonicTimeMicros(void) { | |
| 90 | struct timespec ts; | ||
| 91 | 969196 | clock_gettime(CLOCK_MONOTONIC, &ts); | |
| 92 | 969196 | return ts.tv_sec * 1000000LL + ts.tv_nsec / 1000; | |
| 93 | } | ||
| 94 | |||
| 95 | 275278 | std::string GetTimeStr(std::string fmt) { | |
| 96 | 275278 | auto t = std::time(nullptr); | |
| 97 | struct tm tm; | ||
| 98 | 275278 | localtime_r(&t, &tm); | |
| 99 |
1/2✓ Branch 0 taken 275278 times.
✗ Branch 1 not taken.
|
275278 | std::stringstream sstr; |
| 100 |
1/2✓ Branch 0 taken 275278 times.
✗ Branch 1 not taken.
|
275278 | sstr << std::put_time(&tm, fmt.c_str()); |
| 101 | |||
| 102 |
1/2✓ Branch 0 taken 275278 times.
✗ Branch 1 not taken.
|
550556 | return sstr.str(); |
| 103 | 275278 | } | |
| 104 | |||
| 105 | 137639 | std::string GetDateStr(std::string fmt) { | |
| 106 | 137639 | auto t = std::time(nullptr); | |
| 107 | struct tm tm; | ||
| 108 | 137639 | localtime_r(&t, &tm); | |
| 109 |
1/2✓ Branch 0 taken 137639 times.
✗ Branch 1 not taken.
|
137639 | std::stringstream sstr; |
| 110 |
1/2✓ Branch 0 taken 137639 times.
✗ Branch 1 not taken.
|
137639 | sstr << std::put_time(&tm, fmt.c_str()); |
| 111 | |||
| 112 |
1/2✓ Branch 0 taken 137639 times.
✗ Branch 1 not taken.
|
275278 | return sstr.str(); |
| 113 | 137639 | } | |
| 114 | |||
| 115 | // strerror_r() comes in two flavours: the GNU version returns char* while the | ||
| 116 | // XSI/POSIX version returns int and fills the supplied buffer. These overloads | ||
| 117 | // pick the right behaviour at compile time via the return type. | ||
| 118 | 721 | static inline const char* fppStrerrorResult(char* gnuResult, char* /*buf*/) { | |
| 119 | 721 | return gnuResult; | |
| 120 | } | ||
| 121 | static inline const char* fppStrerrorResult(int xsiResult, char* buf) { | ||
| 122 | return xsiResult == 0 ? buf : "Unknown error"; | ||
| 123 | } | ||
| 124 | 721 | const char* FPPstrerror(int errnum) { | |
| 125 | static thread_local char buf[256]; | ||
| 126 | 721 | buf[0] = '\0'; | |
| 127 | 721 | return fppStrerrorResult(strerror_r(errnum, buf, sizeof(buf)), buf); | |
| 128 | } | ||
| 129 | |||
| 130 | 3133 | int FPPrand() { | |
| 131 |
5/8✓ Branch 0 taken 1885 times.
✓ Branch 1 taken 1248 times.
✓ Branch 2 taken 1885 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1885 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1885 times.
✗ Branch 7 not taken.
|
3133 | static thread_local std::mt19937 engine(std::random_device{}()); |
| 132 |
2/2✓ Branch 0 taken 1885 times.
✓ Branch 1 taken 1248 times.
|
3133 | static thread_local std::uniform_int_distribution<int> dist(0, RAND_MAX); |
| 133 | 3133 | return dist(engine); | |
| 134 | } | ||
| 135 | |||
| 136 | 17904 | bool GetUserIds(const std::string& username, uid_t* uid, gid_t* gid, std::string* homedir) { | |
| 137 | 17904 | long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); | |
| 138 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17904 times.
|
17904 | if (bufsize <= 0) { |
| 139 | ✗ | bufsize = 16384; | |
| 140 | } | ||
| 141 |
1/2✓ Branch 0 taken 17904 times.
✗ Branch 1 not taken.
|
19344 | std::vector<char> buf(bufsize); |
| 142 | struct passwd pwd; | ||
| 143 | 17904 | struct passwd* result = nullptr; | |
| 144 |
1/2✓ Branch 0 taken 17904 times.
✗ Branch 1 not taken.
|
17904 | int rc = getpwnam_r(username.c_str(), &pwd, buf.data(), buf.size(), &result); |
| 145 |
3/4✓ Branch 0 taken 17904 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16464 times.
✓ Branch 3 taken 1440 times.
|
17904 | if (rc != 0 || result == nullptr) { |
| 146 | 16464 | return false; | |
| 147 | } | ||
| 148 |
1/2✓ Branch 0 taken 1440 times.
✗ Branch 1 not taken.
|
1440 | if (uid) { |
| 149 | 1440 | *uid = result->pw_uid; | |
| 150 | } | ||
| 151 |
1/2✓ Branch 0 taken 1440 times.
✗ Branch 1 not taken.
|
1440 | if (gid) { |
| 152 | 1440 | *gid = result->pw_gid; | |
| 153 | } | ||
| 154 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1440 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1440 | if (homedir && result->pw_dir) { |
| 155 | ✗ | *homedir = result->pw_dir; | |
| 156 | } | ||
| 157 | 1440 | return true; | |
| 158 | 17904 | } | |
| 159 | |||
| 160 | /* | ||
| 161 | * Check to see if the specified directory exists | ||
| 162 | */ | ||
| 163 | 4084 | int DirectoryExists(const char* Directory) { | |
| 164 | 4084 | DIR* dir = opendir(Directory); | |
| 165 |
2/2✓ Branch 0 taken 2145 times.
✓ Branch 1 taken 1939 times.
|
4084 | if (dir) { |
| 166 | 2145 | closedir(dir); | |
| 167 | 2145 | return 1; | |
| 168 | } else { | ||
| 169 | 1939 | return 0; | |
| 170 | } | ||
| 171 | } | ||
| 172 | 271 | int DirectoryExists(const std::string& Directory) { | |
| 173 | 271 | return DirectoryExists(Directory.c_str()); | |
| 174 | } | ||
| 175 | |||
| 176 | /* | ||
| 177 | * Check if the specified file exists or not | ||
| 178 | */ | ||
| 179 | 1337323 | int FileExists(const char* File) { | |
| 180 | struct stat sts; | ||
| 181 |
2/2✓ Branch 0 taken 1245892 times.
✓ Branch 1 taken 91431 times.
|
1337323 | if (stat(File, &sts) == -1) { |
| 182 | 1245892 | return 0; | |
| 183 | } else { | ||
| 184 | 91431 | return 1; | |
| 185 | } | ||
| 186 | } | ||
| 187 | 1239451 | int FileExists(const std::string& f) { | |
| 188 | 1239451 | return FileExists(f.c_str()); | |
| 189 | } | ||
| 190 | 19051 | uint64_t FileTimestamp(const std::string& File) { | |
| 191 | struct stat sts; | ||
| 192 |
2/2✓ Branch 0 taken 14105 times.
✓ Branch 1 taken 4946 times.
|
19051 | if (stat(File.c_str(), &sts) == -1) { |
| 193 | 14105 | return 0; | |
| 194 | } else { | ||
| 195 | 4946 | return (uint64_t)sts.st_mtime; | |
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | 520 | int Touch(const std::string& File) { | |
| 200 | 520 | int fd = open(File.c_str(), O_WRONLY | O_CREAT | O_NOCTTY | O_NONBLOCK, 0666); | |
| 201 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 520 times.
|
520 | if (fd < 0) |
| 202 | ✗ | return 0; | |
| 203 | |||
| 204 | 520 | close(fd); | |
| 205 | |||
| 206 | 520 | return 1; | |
| 207 | } | ||
| 208 | |||
| 209 | /* | ||
| 210 | * Get IP address on specified network interface | ||
| 211 | */ | ||
| 212 | 2461 | int GetInterfaceAddress(const char* interface, char* addr, char* mask, char* gw) { | |
| 213 | int fd; | ||
| 214 | struct ifreq ifr; | ||
| 215 | |||
| 216 | 2461 | memset(&ifr, 0, sizeof(ifr)); | |
| 217 | |||
| 218 | 2461 | fd = OpenSocket(AF_INET, SOCK_DGRAM, 0); | |
| 219 | 2461 | ifr.ifr_addr.sa_family = AF_INET; | |
| 220 | |||
| 221 | /* I want IP address attached to E131interface */ | ||
| 222 | 2461 | strncpy(ifr.ifr_name, (const char*)interface, IFNAMSIZ - 1); | |
| 223 | 2461 | int retVal = 0; | |
| 224 |
1/2✓ Branch 0 taken 2461 times.
✗ Branch 1 not taken.
|
2461 | if (addr) { |
| 225 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2437 times.
|
2461 | if (ioctl(fd, SIOCGIFADDR, &ifr)) { |
| 226 | 24 | strcpy(addr, "127.0.0.1"); | |
| 227 | 24 | retVal = 1; | |
| 228 | } else { | ||
| 229 | 2437 | inet_ntop(AF_INET, &((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr, addr, INET_ADDRSTRLEN); | |
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2461 times.
|
2461 | if (mask) { |
| 234 | ✗ | if (ioctl(fd, SIOCGIFNETMASK, &ifr)) | |
| 235 | ✗ | strcpy(mask, "255.255.255.255"); | |
| 236 | else | ||
| 237 | ✗ | inet_ntop(AF_INET, &((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr, mask, INET_ADDRSTRLEN); | |
| 238 | } | ||
| 239 | |||
| 240 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2461 times.
|
2461 | if (gw) { |
| 241 | ✗ | *gw = 0; | |
| 242 | |||
| 243 | FILE* f; | ||
| 244 | char line[100]; | ||
| 245 | char* iface; | ||
| 246 | char* dest; | ||
| 247 | char* route; | ||
| 248 | char* saveptr; | ||
| 249 | |||
| 250 | ✗ | f = fopen("/proc/net/route", "r"); | |
| 251 | |||
| 252 | ✗ | if (f) { | |
| 253 | ✗ | while (fgets(line, 100, f)) { | |
| 254 | ✗ | iface = strtok_r(line, " \t", &saveptr); | |
| 255 | ✗ | dest = strtok_r(NULL, " \t", &saveptr); | |
| 256 | ✗ | route = strtok_r(NULL, " \t", &saveptr); | |
| 257 | |||
| 258 | ✗ | if ((iface && dest && route) && | |
| 259 | ✗ | (!strcmp(iface, interface)) && | |
| 260 | ✗ | (!strcmp(dest, "00000000"))) { | |
| 261 | char* end; | ||
| 262 | ✗ | int ng = strtol(route, &end, 16); | |
| 263 | struct in_addr addr; | ||
| 264 | ✗ | addr.s_addr = ng; | |
| 265 | ✗ | inet_ntop(AF_INET, &addr, gw, INET_ADDRSTRLEN); | |
| 266 | } | ||
| 267 | } | ||
| 268 | ✗ | fclose(f); | |
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 |
1/2✓ Branch 0 taken 2461 times.
✗ Branch 1 not taken.
|
2461 | close(fd); |
| 273 | |||
| 274 | 2461 | return retVal; | |
| 275 | } | ||
| 276 | |||
| 277 | /* | ||
| 278 | * | ||
| 279 | */ | ||
| 280 | |||
| 281 | /* | ||
| 282 | * | ||
| 283 | */ | ||
| 284 | |||
| 285 | static unsigned char bitLookup[16] = { | ||
| 286 | 0x0, | ||
| 287 | 0x8, | ||
| 288 | 0x4, | ||
| 289 | 0xc, | ||
| 290 | 0x2, | ||
| 291 | 0xa, | ||
| 292 | 0x6, | ||
| 293 | 0xe, | ||
| 294 | 0x1, | ||
| 295 | 0x9, | ||
| 296 | 0x5, | ||
| 297 | 0xd, | ||
| 298 | 0x3, | ||
| 299 | 0xb, | ||
| 300 | 0x7, | ||
| 301 | 0xf, | ||
| 302 | }; | ||
| 303 | |||
| 304 | /* | ||
| 305 | * Reverse bits in a byte | ||
| 306 | */ | ||
| 307 | ✗ | uint8_t ReverseBitsInByte(uint8_t n) { | |
| 308 | ✗ | return (bitLookup[n & 0b1111] << 4) | bitLookup[n >> 4]; | |
| 309 | } | ||
| 310 | |||
| 311 | /* | ||
| 312 | * Convert a string of the form "YYYY-MM-DD to an integer YYYYMMDD | ||
| 313 | */ | ||
| 314 | 86175 | int DateStrToInt(const char* str) { | |
| 315 | // Check the length before indexing: a short string put str[7] and | ||
| 316 | // str[10] past its terminator. | ||
| 317 |
4/8✓ Branch 0 taken 86175 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 86175 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 86175 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 86175 times.
|
86175 | if ((!str) || (strnlen(str, 11) != 10) || (str[4] != '-') || (str[7] != '-')) |
| 318 | ✗ | return 0; | |
| 319 | |||
| 320 | 86175 | int result = 0; | |
| 321 | |||
| 322 | 86175 | result += atoi(str) * 10000; // Year | |
| 323 | 86175 | result += atoi(str + 5) * 100; // Month | |
| 324 | 86175 | result += atoi(str + 8); // Day | |
| 325 | |||
| 326 | 86175 | return result; | |
| 327 | } | ||
| 328 | |||
| 329 | /* | ||
| 330 | * Get the current date in an integer form YYYYMMDD | ||
| 331 | */ | ||
| 332 | 10627 | int GetCurrentDateInt(int daysOffset) { | |
| 333 | 10627 | time_t currTime = time(NULL) + (daysOffset * 86400); | |
| 334 | struct tm now; | ||
| 335 | 10627 | int result = 0; | |
| 336 | |||
| 337 | 10627 | localtime_r(&currTime, &now); | |
| 338 | |||
| 339 | 10627 | result += (now.tm_year + 1900) * 10000; | |
| 340 | 10627 | result += (now.tm_mon + 1) * 100; | |
| 341 | 10627 | result += (now.tm_mday); | |
| 342 | |||
| 343 | 10627 | return result; | |
| 344 | } | ||
| 345 | 567903 | std::string secondsToTime(int i) { | |
| 346 |
1/2✓ Branch 0 taken 567903 times.
✗ Branch 1 not taken.
|
567903 | std::stringstream sstr; |
| 347 | |||
| 348 |
2/2✓ Branch 0 taken 81 times.
✓ Branch 1 taken 567822 times.
|
567903 | if (i > (24 * 60 * 60)) { |
| 349 | 81 | int days = i / (24 * 60 * 60); | |
| 350 |
1/2✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
|
81 | sstr << days; |
| 351 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 81 times.
|
81 | if (days == 1) |
| 352 | ✗ | sstr << " day, "; | |
| 353 | else | ||
| 354 |
1/2✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
|
81 | sstr << " days, "; |
| 355 | |||
| 356 | 81 | i = i % (24 * 60 * 60); | |
| 357 | } | ||
| 358 | |||
| 359 |
2/2✓ Branch 0 taken 81 times.
✓ Branch 1 taken 567822 times.
|
567903 | if (i > (60 * 60)) { |
| 360 | 81 | int hour = i / (60 * 60); | |
| 361 |
2/4✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 81 times.
✗ Branch 3 not taken.
|
81 | sstr << std::setw(2) << std::setfill('0') << hour; |
| 362 |
1/2✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
|
81 | sstr << ":"; |
| 363 | 81 | i = i % (60 * 60); | |
| 364 | } | ||
| 365 | |||
| 366 | 567903 | int min = i / 60; | |
| 367 | 567903 | int sec = i % 60; | |
| 368 |
2/4✓ Branch 0 taken 567903 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 567903 times.
✗ Branch 3 not taken.
|
567903 | sstr << std::setw(2) << std::setfill('0') << min; |
| 369 |
1/2✓ Branch 0 taken 567903 times.
✗ Branch 1 not taken.
|
567903 | sstr << ":"; |
| 370 |
2/4✓ Branch 0 taken 567903 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 567903 times.
✗ Branch 3 not taken.
|
567903 | sstr << std::setw(2) << std::setfill('0') << sec; |
| 371 |
1/2✓ Branch 0 taken 567903 times.
✗ Branch 1 not taken.
|
1135806 | return sstr.str(); |
| 372 | 567903 | } | |
| 373 | // fds 0-2 are always open: on /dev/null, when the process was started | ||
| 374 | // without them. This runs before the static initializers, because they open | ||
| 375 | // descriptors too: drogon's main loop (its epoll, timerfd and eventfd) took | ||
| 376 | // 0-2 in an fppd started without them (a "-d" restart closed them before its | ||
| 377 | // exec), and CreateDaemon() then closed them, so nothing on that loop ran | ||
| 378 | // again. | ||
| 379 | 2606 | __attribute__((constructor(101))) static void OpenStandardDescriptors() { | |
| 380 | int fd; | ||
| 381 |
3/6✓ Branch 0 taken 2606 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2606 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2606 times.
|
2606 | while ((fd = open("/dev/null", O_RDWR)) >= 0 && fd <= STDERR_FILENO) { |
| 382 | } | ||
| 383 |
1/2✓ Branch 0 taken 2606 times.
✗ Branch 1 not taken.
|
2606 | if (fd > STDERR_FILENO) { |
| 384 | 2606 | close(fd); | |
| 385 | } | ||
| 386 | 2606 | } | |
| 387 | |||
| 388 | // A descriptor CloseOpenFiles() and MarkOpenFilesCloseOnExec() act on: a | ||
| 389 | // file, unix domain socket or similar, or a TCP/UDP socket. Anything else is | ||
| 390 | // left alone: on OSX it may be a "NPOLICY" descriptor which, if closed, | ||
| 391 | // terminates the process immediately. | ||
| 392 | 1880125890 | static bool isOpenFileOrSocket(int fd) { | |
| 393 |
4/4✓ Branch 0 taken 1880125454 times.
✓ Branch 1 taken 436 times.
✓ Branch 2 taken 1880075987 times.
✓ Branch 3 taken 15189 times.
|
1880125890 | if (fcntl(fd, F_GETFD) == -1) { |
| 394 | 1880110265 | return false; | |
| 395 | } | ||
| 396 | struct stat statBuf; | ||
| 397 |
1/2✓ Branch 0 taken 15625 times.
✗ Branch 1 not taken.
|
15625 | if (fstat(fd, &statBuf) == 0) { |
| 398 | 15625 | return true; | |
| 399 | } | ||
| 400 | struct sockaddr_in address; | ||
| 401 | ✗ | memset(&address, 0, sizeof(address)); | |
| 402 | ✗ | socklen_t addrLen = sizeof(address); | |
| 403 | ✗ | getsockname(fd, (struct sockaddr*)&address, &addrLen); | |
| 404 | ✗ | return address.sin_family != 0; | |
| 405 | } | ||
| 406 | |||
| 407 | /* | ||
| 408 | * Close all open file descriptors (used after fork()) | ||
| 409 | * | ||
| 410 | * stdin/stdout/stderr are left open, daemonMode or not: a daemon's are | ||
| 411 | * /dev/null, and closed, they were taken by the next files the process | ||
| 412 | * opened. daemonMode is kept for plugins that call this. | ||
| 413 | */ | ||
| 414 | 31 | void CloseOpenFiles(int daemonMode) { | |
| 415 | 31 | int maxfd = sysconf(_SC_OPEN_MAX); | |
| 416 |
2/2✓ Branch 0 taken 23070627 times.
✓ Branch 1 taken 31 times.
|
23070658 | for (int fd = 3; fd < maxfd; fd++) { |
| 417 |
2/2✓ Branch 0 taken 2863 times.
✓ Branch 1 taken 23067764 times.
|
23070627 | if (isOpenFileOrSocket(fd)) { |
| 418 | 2863 | close(fd); | |
| 419 | } | ||
| 420 | } | ||
| 421 | 31 | } | |
| 422 | |||
| 423 | /* | ||
| 424 | * The same descriptors as CloseOpenFiles(), marked close-on-exec instead of | ||
| 425 | * closed: for a process that is about to exec or _exit() while other threads | ||
| 426 | * of its own may still be using them, which closing would pull out from under | ||
| 427 | * them (a detached media teardown, GStreamer's and PipeWire's threads), and a | ||
| 428 | * descriptor number then reused would take their writes. | ||
| 429 | */ | ||
| 430 | 1867 | void MarkOpenFilesCloseOnExec() { | |
| 431 | 1867 | int maxfd = sysconf(_SC_OPEN_MAX); | |
| 432 |
2/2✓ Branch 0 taken 1857055263 times.
✓ Branch 1 taken 1867 times.
|
1857057130 | for (int fd = 3; fd < maxfd; fd++) { |
| 433 |
2/2✓ Branch 0 taken 12762 times.
✓ Branch 1 taken 1857042501 times.
|
1857055263 | if (isOpenFileOrSocket(fd)) { |
| 434 | 12762 | int flags = fcntl(fd, F_GETFD); | |
| 435 |
3/4✓ Branch 0 taken 12762 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2310 times.
✓ Branch 3 taken 10452 times.
|
12762 | if (flags != -1 && !(flags & FD_CLOEXEC)) { |
| 436 | 2310 | fcntl(fd, F_SETFD, flags | FD_CLOEXEC); | |
| 437 | } | ||
| 438 | } | ||
| 439 | } | ||
| 440 | 1867 | } | |
| 441 | |||
| 442 | // Redirect our stdout (and everything we fork, which is why this dup2's the fd | ||
| 443 | // rather than just wrapping printf) into logs/fppd.log, prefixing every line | ||
| 444 | // with fppd's own line shape: "<date time.ms> <program>(<pid>) [<facility>] ". | ||
| 445 | // | ||
| 446 | // fppd.log is the merged, sequential record of the system: boot -> init -> fppd | ||
| 447 | // -> restarts -> operations, in one file, in time order. Init used to write its | ||
| 448 | // own fpp_init.log / fpp_boot.log, which could not be interleaved with fppd's | ||
| 449 | // log after the fact -- exactly the problem this arc exists to fix. | ||
| 450 | // | ||
| 451 | // This is a tee, not a redirect: the line is also echoed to the stdout we were | ||
| 452 | // started with. Under systemd that is journald, so `systemctl status fppinit` | ||
| 453 | // and `journalctl -u fpprtc` keep showing what the service did; run by hand from | ||
| 454 | // a shell it is the terminal, so `fppinit setupAudio` still prints as it goes. | ||
| 455 | // Only the fppd.log copy carries the timestamp/program/facility prefix -- the | ||
| 456 | // console copy is the raw line, because journald stamps and tags it already. | ||
| 457 | // | ||
| 458 | // The prefixing is done by bash rather than in C++ because the point of the | ||
| 459 | // dup2 is to capture child output too: the filter has to live on the far side of | ||
| 460 | // the pipe, where tee used to sit. printf '%(...)T' and EPOCHREALTIME are bash | ||
| 461 | // builtins (hence /bin/bash explicitly -- popen uses /bin/sh, which is dash on | ||
| 462 | // Debian and has neither), so this forks nothing per line. Appending per line | ||
| 463 | // rather than holding the file open is what makes it survive a logrotate rename. | ||
| 464 | // | ||
| 465 | // `|| [ -n "$__l" ]` keeps a final line that has no trailing newline: read | ||
| 466 | // returns false at EOF, and that unterminated line is what a process killed | ||
| 467 | // mid-write leaves behind -- the output most worth having. | ||
| 468 | 148 | void teeOutput(const std::string& log, const std::string& program, const std::string& facility, pid_t pid) { | |
| 469 | 148 | std::size_t slash = log.rfind('/'); | |
| 470 |
2/4✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 148 times.
✗ Branch 3 not taken.
|
148 | if (slash != std::string::npos && slash > 0) { |
| 471 | // logs/ under the media dir, and the media dir itself: a first boot can | ||
| 472 | // reach here before either exists. | ||
| 473 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
148 | std::string dir = log.substr(0, slash); |
| 474 | 148 | std::size_t parent = dir.rfind('/'); | |
| 475 |
2/4✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 148 times.
✗ Branch 3 not taken.
|
148 | if (parent != std::string::npos && parent > 0) { |
| 476 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
148 | mkdir(dir.substr(0, parent).c_str(), 0775); |
| 477 | } | ||
| 478 | 148 | mkdir(dir.c_str(), 0775); | |
| 479 | 148 | } | |
| 480 | |||
| 481 | // The bash `>>` below would create the file root-owned (these services run as | ||
| 482 | // root), which then locks out fppd and the PHP-side writers that share it. | ||
| 483 | // Whoever creates fppd.log owns making it group-writable by fpp -- log.cpp | ||
| 484 | // and FPPINIT do the same for the cases where they get there first. | ||
| 485 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 140 times.
|
148 | if (access(log.c_str(), F_OK) != 0) { |
| 486 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | FILE* nf = fopen(log.c_str(), "a"); |
| 487 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (nf) { |
| 488 | uid_t uid; | ||
| 489 | gid_t gid; | ||
| 490 |
7/18✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 8 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
|
24 | if (GetUserIds("fpp", &uid, &gid) && fchown(fileno(nf), uid, gid) == 0) { |
| 491 | 8 | fchmod(fileno(nf), 0664); | |
| 492 | } | ||
| 493 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fclose(nf); |
| 494 | } | ||
| 495 | } | ||
| 496 | |||
| 497 | // Grab a second handle on the console (journald/terminal) BEFORE the dup2 | ||
| 498 | // below takes fd 1 over, so the writer can still reach it. dup() leaves | ||
| 499 | // FD_CLOEXEC clear, so it survives popen's exec into bash and is addressable | ||
| 500 | // there by number -- and it returns the lowest free descriptor, which for a | ||
| 501 | // service this early is a single digit, well clear of the fds bash reserves | ||
| 502 | // for its own redirections. | ||
| 503 | 148 | int consoleFd = dup(STDOUT_FILENO); | |
| 504 | 148 | std::string echoConsole; | |
| 505 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
148 | if (consoleFd >= 0) { |
| 506 |
2/4✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 148 times.
✗ Branch 3 not taken.
|
148 | echoConsole = "printf \"%s\\n\" \"$__l\" >&" + std::to_string(consoleFd) + "; "; |
| 507 | } | ||
| 508 | |||
| 509 | 148 | std::string cmd = "/bin/bash -c 'while IFS= read -r __l || [ -n \"$__l\" ]; do " + | |
| 510 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
296 | echoConsole + |
| 511 | "__now=${EPOCHREALTIME}; __us=${__now#*.}; " | ||
| 512 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
296 | "printf \"%(%Y-%m-%d %H:%M:%S)T.%s " + |
| 513 |
6/12✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 148 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 148 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 148 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 148 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 148 times.
✗ Branch 11 not taken.
|
452 | program + "(" + std::to_string(pid) + ") [" + facility + "] %s\\n\" " + |
| 514 |
3/6✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 148 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 148 times.
✗ Branch 5 not taken.
|
148 | "\"${__now%.*}\" \"${__us:0:3}\" \"$__l\" >> " + log + "; done'"; |
| 515 | |||
| 516 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
148 | FILE* f = popen(cmd.c_str(), "w"); |
| 517 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 148 times.
|
148 | if (!f) { |
| 518 | ✗ | printf("Couldn't start the log writer\n"); | |
| 519 | ✗ | if (consoleFd >= 0) { | |
| 520 | ✗ | close(consoleFd); | |
| 521 | } | ||
| 522 | ✗ | return; | |
| 523 | } | ||
| 524 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 148 times.
|
148 | if (dup2(fileno(f), STDOUT_FILENO) < 0) { |
| 525 | ✗ | printf("Couldn't redirect output to log file\n"); | |
| 526 | } | ||
| 527 | // The writer child has its own copy; ours would otherwise be inherited by | ||
| 528 | // every command we fork from here on. | ||
| 529 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
148 | if (consoleFd >= 0) { |
| 530 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
148 | close(consoleFd); |
| 531 | } | ||
| 532 | 148 | setvbuf(stdout, NULL, _IOLBF, 0); | |
| 533 |
2/4✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 148 times.
✗ Branch 3 not taken.
|
148 | } |
| 534 | |||
| 535 | 51981 | int DateInRange(time_t when, int startDate, int endDate) { | |
| 536 | struct tm dt; | ||
| 537 | 51981 | localtime_r(&when, &dt); | |
| 538 | |||
| 539 | 51981 | int dateInt = 0; | |
| 540 | 51981 | dateInt += (dt.tm_year + 1900) * 10000; | |
| 541 | 51981 | dateInt += (dt.tm_mon + 1) * 100; | |
| 542 | 51981 | dateInt += (dt.tm_mday); | |
| 543 | |||
| 544 |
1/2✓ Branch 0 taken 51981 times.
✗ Branch 1 not taken.
|
103962 | return DateInRange(dateInt, startDate, endDate); |
| 545 | } | ||
| 546 | |||
| 547 | /* | ||
| 548 | * Check to see if current date int is in the range specified | ||
| 549 | */ | ||
| 550 | 344 | int CurrentDateInRange(int startDate, int endDate) { | |
| 551 | 344 | int currentDate = GetCurrentDateInt(); | |
| 552 | |||
| 553 | 344 | return DateInRange(currentDate, startDate, endDate); | |
| 554 | } | ||
| 555 | |||
| 556 | 55532 | int DateInRange(int currentDate, int startDate, int endDate) { | |
| 557 |
3/4✓ Branch 0 taken 13087 times.
✓ Branch 1 taken 42445 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 13087 times.
|
55532 | if ((startDate < 10000) || (endDate < 10000)) { |
| 558 | 42445 | startDate = startDate % 10000; | |
| 559 | 42445 | endDate = endDate % 10000; | |
| 560 | 42445 | currentDate = currentDate % 10000; | |
| 561 | |||
| 562 |
2/2✓ Branch 0 taken 210 times.
✓ Branch 1 taken 42235 times.
|
42445 | if (endDate < startDate) { |
| 563 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 186 times.
|
210 | if (currentDate <= endDate) |
| 564 | 24 | currentDate += 10000; | |
| 565 | |||
| 566 | 210 | endDate += 10000; // next year | |
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 |
2/4✓ Branch 0 taken 55532 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 55532 times.
|
55532 | if ((startDate < 100) || (endDate < 100)) { |
| 571 | ✗ | startDate = startDate % 100; | |
| 572 | ✗ | endDate = endDate % 100; | |
| 573 | ✗ | currentDate = currentDate % 100; | |
| 574 | |||
| 575 | ✗ | if (endDate < startDate) { | |
| 576 | ✗ | if (currentDate <= endDate) | |
| 577 | ✗ | currentDate += 100; | |
| 578 | |||
| 579 | ✗ | endDate += 100; // next month | |
| 580 | } | ||
| 581 | } | ||
| 582 | |||
| 583 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 55532 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
55532 | if ((startDate == 0) && (endDate == 0)) |
| 584 | ✗ | return 1; | |
| 585 | |||
| 586 |
4/4✓ Branch 0 taken 33593 times.
✓ Branch 1 taken 21939 times.
✓ Branch 2 taken 6164 times.
✓ Branch 3 taken 27429 times.
|
55532 | if ((startDate <= currentDate) && (currentDate <= endDate)) |
| 587 | 6164 | return 1; | |
| 588 | |||
| 589 | 49368 | return 0; | |
| 590 | } | ||
| 591 | |||
| 592 | ✗ | std::string tail(std::string const& source, size_t const length) { | |
| 593 | ✗ | if (length >= source.size()) | |
| 594 | ✗ | return source; | |
| 595 | |||
| 596 | ✗ | return source.substr(source.size() - length); | |
| 597 | } | ||
| 598 | |||
| 599 | /* | ||
| 600 | * Helpers to split a string on the specified character delimiter | ||
| 601 | */ | ||
| 602 | 146081 | std::vector<std::string>& split(const std::string& s, char delim, std::vector<std::string>& elems) { | |
| 603 |
1/2✓ Branch 0 taken 146081 times.
✗ Branch 1 not taken.
|
146081 | std::stringstream ss(s); |
| 604 | 146081 | std::string item; | |
| 605 | |||
| 606 |
4/6✓ Branch 0 taken 733632 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 733632 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 587551 times.
✓ Branch 5 taken 146081 times.
|
733632 | while (getline(ss, item, delim)) { |
| 607 |
1/2✓ Branch 0 taken 587551 times.
✗ Branch 1 not taken.
|
587551 | elems.push_back(item); |
| 608 | } | ||
| 609 | |||
| 610 | 146081 | return elems; | |
| 611 | 146081 | } | |
| 612 | |||
| 613 | 146081 | std::vector<std::string> split(const std::string& s, char delim) { | |
| 614 | 146081 | std::vector<std::string> elems; | |
| 615 | |||
| 616 |
1/2✓ Branch 0 taken 146081 times.
✗ Branch 1 not taken.
|
146081 | split(s, delim, elems); |
| 617 | |||
| 618 | 146081 | return elems; | |
| 619 | ✗ | } | |
| 620 | |||
| 621 | 693 | bool ParseTimeString(const std::string& str, int& hour, int& minute, int& second) { | |
| 622 |
1/2✓ Branch 0 taken 693 times.
✗ Branch 1 not taken.
|
693 | std::vector<std::string> parts = split(str, ':'); |
| 623 | |||
| 624 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 692 times.
|
693 | if (parts.size() < 2) { |
| 625 | 1 | return false; | |
| 626 | } | ||
| 627 | |||
| 628 | 692 | hour = atoi(parts[0].c_str()); | |
| 629 | 692 | minute = atoi(parts[1].c_str()); | |
| 630 |
2/2✓ Branch 0 taken 691 times.
✓ Branch 1 taken 1 times.
|
692 | second = parts.size() > 2 ? atoi(parts[2].c_str()) : 0; |
| 631 | |||
| 632 | 692 | return true; | |
| 633 | 693 | } | |
| 634 | |||
| 635 | 633 | inline std::string dequote(const std::string& s) { | |
| 636 |
8/10✓ Branch 0 taken 604 times.
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 604 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 601 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 601 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 601 times.
✓ Branch 9 taken 32 times.
|
633 | if (s.length() > 2 && (s[0] == '\'' || s[0] == '"') && s[0] == s[s.length() - 1]) { |
| 637 | 601 | return s.substr(1, s.length() - 2); | |
| 638 | } | ||
| 639 | 32 | return s; | |
| 640 | } | ||
| 641 | |||
| 642 | 25 | std::vector<std::string> splitWithQuotes(const std::string& s, char delim) { | |
| 643 | 25 | std::vector<std::string> ret; | |
| 644 | 25 | const char* mystart = s.c_str(); | |
| 645 | 25 | bool instring = false; | |
| 646 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 25 times.
|
60 | for (const char* p = mystart; *p; p++) { |
| 647 |
3/4✓ Branch 0 taken 33 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
|
35 | if (*p == '"' || *p == '\'') { |
| 648 | 2 | instring = !instring; | |
| 649 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
|
33 | } else if (*p == delim && !instring) { |
| 650 |
3/6✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | ret.push_back(dequote(std::string(mystart, p - mystart))); |
| 651 | 3 | mystart = p + 1; | |
| 652 | } | ||
| 653 | } | ||
| 654 |
3/6✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25 times.
✗ Branch 5 not taken.
|
25 | ret.push_back(dequote(std::string(mystart))); |
| 655 | 25 | return ret; | |
| 656 | ✗ | } | |
| 657 | |||
| 658 | // Upper bound on what GetFileContents() returns. Its callers read configs, | ||
| 659 | // sysfs/proc nodes and small data files; this stops a runaway file or a | ||
| 660 | // device node from being read into an allocation that throws. | ||
| 661 | static constexpr size_t MAX_FILE_CONTENTS = 64 * 1024 * 1024; | ||
| 662 | |||
| 663 | 80792 | std::string GetFileContents(const std::string& filename) { | |
| 664 | 80792 | FILE* fd = fopen(filename.c_str(), "r"); | |
| 665 | 80792 | std::string contents; | |
| 666 |
2/2✓ Branch 0 taken 68374 times.
✓ Branch 1 taken 12418 times.
|
80792 | if (fd != nullptr) { |
| 667 | // A directory opens fine for reading on Linux, and its seek-to-end | ||
| 668 | // offset is not a size (ext4 reports LLONG_MAX), which made the | ||
| 669 | // resize below throw. | ||
| 670 | struct stat st; | ||
| 671 |
5/6✓ Branch 0 taken 68374 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 68373 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 68373 times.
|
68374 | if (fstat(fileno(fd), &st) != 0 || S_ISDIR(st.st_mode)) { |
| 672 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | fclose(fd); |
| 673 | 1 | return contents; | |
| 674 | } | ||
| 675 | 68373 | flock(fileno(fd), LOCK_SH); | |
| 676 | // st_size is only a hint: /proc reports 0 and sysfs 4096 whatever | ||
| 677 | // the real length, and the file can change under us. Read to EOF | ||
| 678 | // and keep what fread actually returned. | ||
| 679 |
4/6✓ Branch 0 taken 68373 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 54247 times.
✓ Branch 3 taken 14126 times.
✓ Branch 4 taken 54247 times.
✗ Branch 5 not taken.
|
68373 | if (S_ISREG(st.st_mode) && st.st_size > 0 && (uint64_t)st.st_size <= MAX_FILE_CONTENTS) { |
| 680 |
1/2✓ Branch 0 taken 54247 times.
✗ Branch 1 not taken.
|
54247 | contents.reserve(st.st_size); |
| 681 | } | ||
| 682 | char buf[4096]; | ||
| 683 | size_t i; | ||
| 684 |
3/4✓ Branch 0 taken 191542 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 123169 times.
✓ Branch 3 taken 68373 times.
|
191542 | while ((i = fread(buf, 1, sizeof(buf), fd)) > 0) { |
| 685 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123169 times.
|
123169 | if (contents.size() + i > MAX_FILE_CONTENTS) { |
| 686 | ✗ | fprintf(stderr, "ERROR: %s is larger than %zu bytes, not reading it.\n", filename.c_str(), MAX_FILE_CONTENTS); | |
| 687 | ✗ | contents.clear(); | |
| 688 | ✗ | break; | |
| 689 | } | ||
| 690 |
1/2✓ Branch 0 taken 123169 times.
✗ Branch 1 not taken.
|
123169 | contents.append(buf, i); |
| 691 | } | ||
| 692 | 68373 | flock(fileno(fd), LOCK_UN); | |
| 693 |
1/2✓ Branch 0 taken 68373 times.
✗ Branch 1 not taken.
|
68373 | fclose(fd); |
| 694 | 68373 | int x = contents.size() - 1; | |
| 695 |
2/2✓ Branch 0 taken 68751 times.
✓ Branch 1 taken 1 times.
|
68752 | for (; x > 0; x--) { |
| 696 |
2/2✓ Branch 0 taken 68372 times.
✓ Branch 1 taken 379 times.
|
68751 | if (contents[x] != 0) { |
| 697 | 68372 | break; | |
| 698 | } | ||
| 699 | } | ||
| 700 |
1/2✓ Branch 0 taken 68373 times.
✗ Branch 1 not taken.
|
68373 | contents.resize(x + 1); |
| 701 | } | ||
| 702 | 80791 | return contents; | |
| 703 | ✗ | } | |
| 704 | |||
| 705 | 18114 | bool PutFileContents(const std::string& filename, const std::string& str) { | |
| 706 | // Lock before truncating and write unbuffered, so a LOCK_SH reader never | ||
| 707 | // sees the file empty or half written. | ||
| 708 |
1/2✓ Branch 0 taken 18114 times.
✗ Branch 1 not taken.
|
18114 | int fd = open(filename.c_str(), O_WRONLY | O_CREAT, 0666); |
| 709 |
2/2✓ Branch 0 taken 516 times.
✓ Branch 1 taken 17598 times.
|
18114 | if (fd == -1) { |
| 710 |
1/2✓ Branch 0 taken 516 times.
✗ Branch 1 not taken.
|
516 | fprintf(stderr, "ERROR: Unable to open %s for writing.\n", filename.c_str()); |
| 711 | 516 | return false; | |
| 712 | } | ||
| 713 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 17598 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 17598 times.
|
17598 | while (flock(fd, LOCK_EX) == -1 && errno == EINTR) { |
| 714 | } | ||
| 715 | // Only regular files can be truncated; device nodes and FIFOs are just written. | ||
| 716 | 17598 | bool ok = true; | |
| 717 | 17598 | int err = 0; | |
| 718 | struct stat st; | ||
| 719 |
4/8✓ Branch 0 taken 17598 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17598 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 17598 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 17598 times.
|
17598 | if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && ftruncate(fd, 0) != 0) { |
| 720 | ✗ | ok = false; | |
| 721 | ✗ | err = errno; | |
| 722 | } | ||
| 723 | 17598 | const char* p = str.data(); | |
| 724 | 17598 | size_t left = str.size(); | |
| 725 |
3/4✓ Branch 0 taken 35193 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17595 times.
✓ Branch 3 taken 17598 times.
|
35193 | while (ok && left > 0) { |
| 726 |
1/2✓ Branch 0 taken 17595 times.
✗ Branch 1 not taken.
|
17595 | ssize_t n = write(fd, p, left); |
| 727 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17595 times.
|
17595 | if (n < 0) { |
| 728 | ✗ | if (errno == EINTR) { | |
| 729 | ✗ | continue; | |
| 730 | } | ||
| 731 | ✗ | ok = false; | |
| 732 | ✗ | err = errno; | |
| 733 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17595 times.
|
17595 | } else if (n == 0) { |
| 734 | // A sysfs store() returning 0 makes no progress; don't spin on it. | ||
| 735 | ✗ | ok = false; | |
| 736 | ✗ | err = EIO; | |
| 737 | } else { | ||
| 738 | 17595 | p += n; | |
| 739 | 17595 | left -= n; | |
| 740 | } | ||
| 741 | } | ||
| 742 | 17598 | flock(fd, LOCK_UN); | |
| 743 |
1/2✓ Branch 0 taken 17598 times.
✗ Branch 1 not taken.
|
17598 | close(fd); |
| 744 |
1/2✓ Branch 0 taken 17598 times.
✗ Branch 1 not taken.
|
17598 | SetFilePerms(filename); |
| 745 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17598 times.
|
17598 | if (!ok) { |
| 746 | ✗ | fprintf(stderr, "ERROR: Unable to write %s: %s\n", filename.c_str(), strerror(err)); | |
| 747 | } | ||
| 748 | 17598 | return ok; | |
| 749 | } | ||
| 750 | 195 | bool CopyFileContents(const std::string& srcFile, const std::string& destFile) { | |
| 751 | int input, output; | ||
| 752 |
3/4✓ Branch 0 taken 195 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 194 times.
|
195 | if ((input = open(srcFile.c_str(), O_RDONLY)) == -1) { |
| 753 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | fprintf(stderr, "ERROR: Unable to open %s for reading.\n", srcFile.c_str()); |
| 754 | 1 | return false; | |
| 755 | } | ||
| 756 |
3/4✓ Branch 0 taken 194 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
✓ Branch 3 taken 150 times.
|
194 | if ((output = creat(destFile.c_str(), 0660)) == -1) { |
| 757 |
1/2✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
|
44 | close(input); |
| 758 | 44 | return false; | |
| 759 | } | ||
| 760 | // Here we use kernel-space copying for performance | ||
| 761 | #if defined(__APPLE__) | ||
| 762 | int result = fcopyfile(input, output, 0, COPYFILE_ALL); | ||
| 763 | bool ok = result == 0; | ||
| 764 | #else | ||
| 765 | 150 | off_t bytesCopied = 0; | |
| 766 | 150 | struct stat fileinfo = { 0 }; | |
| 767 | 150 | fstat(input, &fileinfo); | |
| 768 | // sendfile returns the number of bytes copied, not 0, on success | ||
| 769 | 150 | ssize_t result = sendfile(output, input, &bytesCopied, fileinfo.st_size); | |
| 770 | 150 | bool ok = result == fileinfo.st_size; | |
| 771 | #endif | ||
| 772 |
1/2✓ Branch 0 taken 150 times.
✗ Branch 1 not taken.
|
150 | close(input); |
| 773 |
1/2✓ Branch 0 taken 150 times.
✗ Branch 1 not taken.
|
150 | close(output); |
| 774 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 150 times.
|
150 | if (!ok) { |
| 775 | ✗ | fprintf(stderr, "ERROR: Unable to copy %s to %s.\n", srcFile.c_str(), destFile.c_str()); | |
| 776 | } | ||
| 777 | 150 | return ok; | |
| 778 | } | ||
| 779 | |||
| 780 | 17893 | bool SetFilePerms(const std::string& filename, bool exBit) { | |
| 781 | 17893 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; | |
| 782 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 17875 times.
|
17893 | if (exBit) { |
| 783 | 18 | mode |= S_IRWXU | S_IRWXG | S_IXOTH; | |
| 784 | } | ||
| 785 | 17893 | chmod(filename.c_str(), mode); | |
| 786 | #ifndef PLATFORM_OSX | ||
| 787 | uid_t uid; | ||
| 788 | gid_t gid; | ||
| 789 |
4/6✓ Branch 0 taken 17893 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17893 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1430 times.
✓ Branch 5 taken 16463 times.
|
35786 | if (GetUserIds("fpp", &uid, &gid)) { |
| 790 | 1430 | chown(filename.c_str(), uid, gid); | |
| 791 | } | ||
| 792 | #endif | ||
| 793 | |||
| 794 | 17893 | return true; | |
| 795 | } | ||
| 796 | |||
| 797 | 15 | bool SetFilePerms(const char* file, bool exBit) { | |
| 798 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | const std::string filename = file; |
| 799 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
30 | return SetFilePerms(filename, exBit); |
| 800 | 15 | } | |
| 801 | |||
| 802 | ///////////////////////////////////////////////////////////////////////////// | ||
| 803 | // trim from start (in place) | ||
| 804 | 761656 | static inline void ltrim(std::string& s) { | |
| 805 |
2/4✓ Branch 0 taken 761656 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 761656 times.
✗ Branch 3 not taken.
|
761656 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { |
| 806 | 761590 | return !std::isspace(ch); | |
| 807 | })); | ||
| 808 | 761656 | } | |
| 809 | // trim from end (in place) | ||
| 810 | 761656 | static inline void rtrim(std::string& s) { | |
| 811 |
2/4✓ Branch 0 taken 761656 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 761656 times.
✗ Branch 3 not taken.
|
1534626 | s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { |
| 812 | 758581 | return !std::isspace(ch); | |
| 813 | 761656 | }).base(), | |
| 814 | 761656 | s.end()); | |
| 815 | 761656 | } | |
| 816 | // trim from both ends (in place) | ||
| 817 | 761656 | void TrimWhiteSpace(std::string& s) { | |
| 818 | 761656 | ltrim(s); | |
| 819 | 761656 | rtrim(s); | |
| 820 | 761656 | } | |
| 821 | |||
| 822 | 3220204 | bool startsWith(const std::string& str, const std::string& prefix) { | |
| 823 |
4/4✓ Branch 0 taken 3104356 times.
✓ Branch 1 taken 115848 times.
✓ Branch 2 taken 228454 times.
✓ Branch 3 taken 2875902 times.
|
3220204 | return ((prefix.size() <= str.size()) && std::equal(prefix.begin(), prefix.end(), str.begin())); |
| 824 | } | ||
| 825 | 426797 | bool endsWith(const std::string& str, const std::string& suffix) { | |
| 826 |
4/4✓ Branch 0 taken 230280 times.
✓ Branch 1 taken 196517 times.
✓ Branch 2 taken 3582 times.
✓ Branch 3 taken 226698 times.
|
426797 | return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix); |
| 827 | } | ||
| 828 | 216 | bool IsShowEthernetInterface(const std::string& dev) { | |
| 829 | // kernel ethernet naming only (eth0, eno1, enp3s0, enx<mac>, end0). | ||
| 830 | // Deliberately not every 'e' name: erspan0 or an admin's e-named | ||
| 831 | // bridge/veth must not get its qdisc replaced or its drops counted as | ||
| 832 | // FPP show-traffic drops. | ||
| 833 |
13/28✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 216 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 144 times.
✓ Branch 5 taken 72 times.
✓ Branch 6 taken 144 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 144 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 144 times.
✓ Branch 12 taken 144 times.
✓ Branch 13 taken 72 times.
✓ Branch 14 taken 144 times.
✓ Branch 15 taken 72 times.
✓ Branch 16 taken 216 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 216 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
|
936 | return startsWith(dev, "eth") || startsWith(dev, "en"); |
| 834 | } | ||
| 835 | 1584 | std::string TcPath() { | |
| 836 |
3/6✓ Branch 0 taken 1584 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1584 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1544 times.
✗ Branch 5 not taken.
|
3208 | return FileExists("/usr/sbin/tc") ? "/usr/sbin/tc" : "/sbin/tc"; |
| 837 | } | ||
| 838 | 23 | static std::string currentRootQdisc(const std::string& dev) { | |
| 839 |
4/8✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
|
23 | std::string cmd = TcPath() + " qdisc show dev " + dev + " 2>/dev/null"; |
| 840 | 23 | std::string ret; | |
| 841 |
1/2✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
|
23 | FILE* pipe = popen(cmd.c_str(), "r"); |
| 842 |
1/2✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
|
23 | if (pipe) { |
| 843 | char buf[256]; | ||
| 844 |
2/4✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
|
23 | while (fgets(buf, sizeof(buf), pipe)) { |
| 845 |
2/4✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
|
23 | if (strstr(buf, " root ") && strncmp(buf, "qdisc ", 6) == 0) { |
| 846 | 23 | char* start = &buf[6]; | |
| 847 | 23 | char* end = strchr(start, ' '); | |
| 848 |
1/2✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
|
23 | if (end) { |
| 849 |
1/2✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
|
46 | ret = std::string(start, end - start); |
| 850 | } | ||
| 851 | 23 | break; | |
| 852 | } | ||
| 853 | } | ||
| 854 |
1/2✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
|
23 | pclose(pipe); |
| 855 | } | ||
| 856 | 23 | return ret; | |
| 857 | 23 | } | |
| 858 | 23 | bool InstallShowTrafficQdisc(const std::string& dev) { | |
| 859 | // fq enables per-socket pacing (SO_MAX_PACING_RATE). flow_limit must | ||
| 860 | // exceed the packets one paced socket can queue (wmem_max ~768KB | ||
| 861 | // effective / ~2KB skb truesize ~= 330) or fq silently drops the tail | ||
| 862 | // of large frames at enqueue, invisibly to the sender. | ||
| 863 |
1/2✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
|
23 | std::string cur = currentRootQdisc(dev); |
| 864 |
3/4✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 4 times.
|
23 | if (cur == "fq") { |
| 865 | 19 | return true; | |
| 866 | } | ||
| 867 |
11/26✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 4 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 4 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 4 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✓ Branch 25 taken 4 times.
|
4 | if (cur != "" && cur != "pfifo_fast" && cur != "fq_codel" && cur != "mq" && cur != "noqueue" && cur != "pfifo") { |
| 868 | // an admin deliberately installed something (htb/tbf/cake/prio/...); | ||
| 869 | // leave it alone - pacing will just fall back to drain-based sending | ||
| 870 | ✗ | return false; | |
| 871 | } | ||
| 872 |
4/8✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
|
4 | std::string cmd = TcPath() + " qdisc replace dev " + dev + " root fq flow_limit 1000 > /dev/null 2>&1"; |
| 873 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | return system(cmd.c_str()) == 0; |
| 874 | 23 | } | |
| 875 | 27 | bool InterfaceHasRoutableIPv4(const std::string& dev) { | |
| 876 | struct ifaddrs* ifaddr; | ||
| 877 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
|
27 | if (getifaddrs(&ifaddr) != 0) { |
| 878 | ✗ | return false; | |
| 879 | } | ||
| 880 | 27 | bool found = false; | |
| 881 |
2/2✓ Branch 0 taken 97 times.
✓ Branch 1 taken 4 times.
|
101 | for (struct ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) { |
| 882 |
8/10✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 51 times.
✓ Branch 4 taken 46 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✓ Branch 7 taken 23 times.
✓ Branch 8 taken 23 times.
✓ Branch 9 taken 74 times.
|
97 | if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET && dev == ifa->ifa_name) { |
| 883 | 23 | uint32_t addr = ntohl(((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr); | |
| 884 | // 169.254.0.0/16 link-local is what every unconfigured interface | ||
| 885 | // gets from the catch-all networkd config; not a show network | ||
| 886 |
2/4✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
|
23 | if ((addr & 0xFFFF0000) != 0xA9FE0000 && addr != 0) { |
| 887 | 23 | found = true; | |
| 888 | 23 | break; | |
| 889 | } | ||
| 890 | } | ||
| 891 | } | ||
| 892 | 27 | freeifaddrs(ifaddr); | |
| 893 | 27 | return found; | |
| 894 | } | ||
| 895 | 21025 | bool contains(const std::string& str, const std::string& v) { | |
| 896 | 21025 | return str.find(v) != std::string::npos; | |
| 897 | } | ||
| 898 | 144191 | void replaceAll(std::string& str, const std::string& from, const std::string& to) { | |
| 899 | 144191 | size_t start_pos = 0; | |
| 900 |
2/2✓ Branch 0 taken 141216 times.
✓ Branch 1 taken 144191 times.
|
285407 | while ((start_pos = str.find(from, start_pos)) != std::string::npos) { |
| 901 | 141216 | str.replace(start_pos, from.length(), to); | |
| 902 | 141216 | start_pos += to.length(); // ... | |
| 903 | } | ||
| 904 | 144191 | } | |
| 905 | |||
| 906 | 226422 | bool replaceStart(std::string& str, const std::string& from, const std::string& to) { | |
| 907 |
2/2✓ Branch 0 taken 2970 times.
✓ Branch 1 taken 223452 times.
|
226422 | if (!startsWith(str, from)) |
| 908 | 2970 | return false; | |
| 909 | |||
| 910 | 223452 | str.replace(0, from.size(), to); | |
| 911 | 223452 | return true; | |
| 912 | } | ||
| 913 | |||
| 914 | ✗ | bool replaceEnd(std::string& str, const std::string& from, const std::string& to) { | |
| 915 | ✗ | if (!endsWith(str, from)) | |
| 916 | ✗ | return false; | |
| 917 | |||
| 918 | ✗ | str.replace(str.size() - from.size(), from.size(), to); | |
| 919 | ✗ | return true; | |
| 920 | } | ||
| 921 | |||
| 922 | ✗ | void toUpper(std::string& str) { | |
| 923 | ✗ | std::transform(str.begin(), str.end(), str.begin(), ::toupper); | |
| 924 | ✗ | } | |
| 925 | 22064 | void toLower(std::string& str) { | |
| 926 | 22064 | std::transform(str.begin(), str.end(), str.begin(), ::tolower); | |
| 927 | 22064 | } | |
| 928 | ✗ | std::string toUpperCopy(const std::string& str) { | |
| 929 | ✗ | std::string cp = str; | |
| 930 | ✗ | toUpper(cp); | |
| 931 | ✗ | return cp; | |
| 932 | ✗ | } | |
| 933 | 22064 | std::string toLowerCopy(const std::string& str) { | |
| 934 | 22064 | std::string cp = str; | |
| 935 |
1/2✓ Branch 0 taken 22064 times.
✗ Branch 1 not taken.
|
22064 | toLower(cp); |
| 936 | 22064 | return cp; | |
| 937 | ✗ | } | |
| 938 | |||
| 939 | 3082 | const std::string& getRawMediaDir() { | |
| 940 | 166 | static const std::string dir = [] { | |
| 941 | 166 | const char* e = getenv("FPP_MEDIA_DIR"); | |
| 942 |
4/6✓ Branch 0 taken 154 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 154 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 166 times.
✗ Branch 5 not taken.
|
420 | return std::string(e && *e ? e : "/home/fpp/media"); |
| 943 |
4/8✓ Branch 0 taken 166 times.
✓ Branch 1 taken 2916 times.
✓ Branch 2 taken 166 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 166 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
3082 | }(); |
| 944 | 3082 | return dir; | |
| 945 | } | ||
| 946 | |||
| 947 | 2459 | bool getRawSetting(const std::string& setting, std::string& value) { | |
| 948 |
2/4✓ Branch 0 taken 2459 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2459 times.
✗ Branch 3 not taken.
|
2459 | const std::string settingsFile = getRawMediaDir() + "/settings"; |
| 949 |
3/4✓ Branch 0 taken 2459 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 268 times.
✓ Branch 3 taken 2191 times.
|
2459 | if (!FileExists(settingsFile)) { |
| 950 | 268 | return false; | |
| 951 | } | ||
| 952 |
1/2✓ Branch 0 taken 2191 times.
✗ Branch 1 not taken.
|
2191 | std::string c = GetFileContents(settingsFile); |
| 953 |
1/2✓ Branch 0 taken 2191 times.
✗ Branch 1 not taken.
|
2191 | size_t idx = c.find(setting + " = "); |
| 954 |
2/2✓ Branch 0 taken 605 times.
✓ Branch 1 taken 1586 times.
|
2191 | if (idx != std::string::npos) { |
| 955 | 605 | int offset = idx + setting.size() + 3; | |
| 956 | 605 | size_t idx2 = c.find("\r", offset); | |
| 957 | 605 | size_t idx3 = c.find("\n", offset); | |
| 958 |
1/2✓ Branch 0 taken 605 times.
✗ Branch 1 not taken.
|
605 | if (idx2 == std::string::npos) { |
| 959 | 605 | idx2 = idx3; | |
| 960 | } | ||
| 961 |
2/4✓ Branch 0 taken 605 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 605 times.
|
605 | if (idx3 != std::string::npos && idx3 < idx2) { |
| 962 | ✗ | idx2 = idx3; | |
| 963 | } | ||
| 964 |
1/2✓ Branch 0 taken 605 times.
✗ Branch 1 not taken.
|
605 | std::string s = c.substr(offset, idx2 - offset); |
| 965 |
1/2✓ Branch 0 taken 605 times.
✗ Branch 1 not taken.
|
605 | TrimWhiteSpace(s); |
| 966 |
1/2✓ Branch 0 taken 605 times.
✗ Branch 1 not taken.
|
605 | value = dequote(s); |
| 967 | 605 | return true; | |
| 968 | 605 | } | |
| 969 | 1586 | return false; | |
| 970 | 2459 | } | |
| 971 | 1294 | int getRawSettingInt(const std::string& setting, int def) { | |
| 972 | // Parse with strtol and check where it stopped: the hand-rolled digit | ||
| 973 | // loop turned "" into -14, read past the end of a file without a final | ||
| 974 | // newline, and accepted any junk as digits. | ||
| 975 | 1294 | std::string s; | |
| 976 |
3/4✓ Branch 0 taken 1294 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 896 times.
✓ Branch 3 taken 398 times.
|
1294 | if (!getRawSetting(setting, s)) { |
| 977 | 896 | return def; | |
| 978 | } | ||
| 979 |
1/2✓ Branch 0 taken 398 times.
✗ Branch 1 not taken.
|
398 | TrimWhiteSpace(s); |
| 980 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 398 times.
|
398 | if (s.empty()) { |
| 981 | ✗ | return def; | |
| 982 | } | ||
| 983 | 398 | errno = 0; | |
| 984 | 398 | char* end = nullptr; | |
| 985 | 398 | long v = strtol(s.c_str(), &end, 10); | |
| 986 |
10/12✓ Branch 0 taken 397 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 397 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 391 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 391 times.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 391 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 391 times.
|
398 | if (end == s.c_str() || *end != '\0' || errno == ERANGE || v < INT_MIN || v > INT_MAX) { |
| 987 | 1 | return def; | |
| 988 | } | ||
| 989 | 397 | return (int)v; | |
| 990 | 1294 | } | |
| 991 | |||
| 992 | 473 | void setRawSetting(const std::string& setting, const std::string& value) { | |
| 993 |
2/4✓ Branch 0 taken 473 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 473 times.
✗ Branch 3 not taken.
|
473 | const std::string settingsFile = getRawMediaDir() + "/settings"; |
| 994 |
1/2✓ Branch 0 taken 473 times.
✗ Branch 1 not taken.
|
473 | std::string content = GetFileContents(settingsFile); |
| 995 |
1/2✓ Branch 0 taken 473 times.
✗ Branch 1 not taken.
|
473 | std::vector<std::string> lines = split(content, '\n'); |
| 996 |
1/2✓ Branch 0 taken 473 times.
✗ Branch 1 not taken.
|
473 | content = ""; |
| 997 |
2/2✓ Branch 0 taken 2449 times.
✓ Branch 1 taken 473 times.
|
2922 | for (auto& line : lines) { |
| 998 |
3/4✓ Branch 0 taken 2449 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2365 times.
✓ Branch 3 taken 84 times.
|
2449 | if (line.find(setting + " = ") == std::string::npos) { |
| 999 |
1/2✓ Branch 0 taken 2365 times.
✗ Branch 1 not taken.
|
2365 | content += line; |
| 1000 |
1/2✓ Branch 0 taken 2365 times.
✗ Branch 1 not taken.
|
2365 | content += "\n"; |
| 1001 | } | ||
| 1002 | } | ||
| 1003 |
1/2✓ Branch 0 taken 473 times.
✗ Branch 1 not taken.
|
473 | content += setting; |
| 1004 |
1/2✓ Branch 0 taken 473 times.
✗ Branch 1 not taken.
|
473 | content += " = \""; |
| 1005 |
1/2✓ Branch 0 taken 473 times.
✗ Branch 1 not taken.
|
473 | content += value; |
| 1006 |
1/2✓ Branch 0 taken 473 times.
✗ Branch 1 not taken.
|
473 | content += "\"\n"; |
| 1007 |
1/2✓ Branch 0 taken 473 times.
✗ Branch 1 not taken.
|
473 | PutFileContents(settingsFile, content); |
| 1008 | 473 | } | |
| 1009 | |||
| 1010 | 106 | std::map<std::string, std::string> loadSettingsFile(const std::string& filename) { | |
| 1011 | 106 | std::map<std::string, std::string> ret; | |
| 1012 |
1/2✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
|
106 | std::string content = GetFileContents(filename); |
| 1013 |
1/2✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
|
106 | std::vector<std::string> lines = split(content, '\n'); |
| 1014 |
2/2✓ Branch 0 taken 355 times.
✓ Branch 1 taken 106 times.
|
461 | for (auto& line : lines) { |
| 1015 | 355 | size_t idx = line.find("="); | |
| 1016 |
1/2✓ Branch 0 taken 355 times.
✗ Branch 1 not taken.
|
355 | if (idx != std::string::npos) { |
| 1017 |
1/2✓ Branch 0 taken 355 times.
✗ Branch 1 not taken.
|
355 | std::string key = line.substr(0, idx); |
| 1018 |
1/2✓ Branch 0 taken 355 times.
✗ Branch 1 not taken.
|
355 | std::string value = line.substr(idx + 1); |
| 1019 |
1/2✓ Branch 0 taken 355 times.
✗ Branch 1 not taken.
|
355 | TrimWhiteSpace(key); |
| 1020 |
1/2✓ Branch 0 taken 355 times.
✗ Branch 1 not taken.
|
355 | TrimWhiteSpace(value); |
| 1021 |
3/6✓ Branch 0 taken 355 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 355 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 355 times.
✗ Branch 5 not taken.
|
355 | if (!value.empty() && value[0] == '"') { |
| 1022 |
1/2✓ Branch 0 taken 355 times.
✗ Branch 1 not taken.
|
355 | value = value.substr(1); |
| 1023 |
1/2✓ Branch 0 taken 355 times.
✗ Branch 1 not taken.
|
355 | value = value.substr(0, value.size() - 1); |
| 1024 | } | ||
| 1025 |
2/4✓ Branch 0 taken 355 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 355 times.
✗ Branch 3 not taken.
|
355 | ret[key] = value; |
| 1026 | 355 | } | |
| 1027 | } | ||
| 1028 | 106 | return ret; | |
| 1029 | 106 | } | |
| 1030 |