GCC Code Coverage Report


Directory: ./
File: non-gpl/CapeUtils/fppcapedetect.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 53 60 88.3%
Functions: 2 2 100.0%
Branches: 66 100 66.0%

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 CC-BY-ND as described in the
10 * included LICENSE.CC-BY-ND file. This file may be modified for
11 * personal use, but modified copies MAY NOT be redistributed in any form.
12 */
13
14 #include <dirent.h>
15 #include <filesystem>
16 #include <grp.h>
17 #include <pwd.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21
22 #include "CapeUtils.h"
23
24 #include <string>
25
26 61 int remove_recursive(const char* const path, bool removeThis = true) {
27 61 DIR* const directory = opendir(path);
28
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 if (directory) {
29 struct dirent* entry;
30
2/2
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 61 times.
230 while ((entry = readdir(directory))) {
31
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 47 times.
108 if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) {
32 122 continue;
33 }
34 47 char filename[strlen(path) + strlen(entry->d_name) + 2];
35 47 sprintf(filename, "%s/%s", path, entry->d_name);
36
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 41 times.
47 if (entry->d_type == DT_DIR) {
37
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (remove_recursive(filename)) {
38 ✗ closedir(directory);
39 ✗ return -1;
40 }
41 } else {
42
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41 times.
41 if (remove(filename)) {
43 ✗ closedir(directory);
44 ✗ return -1;
45 }
46 }
47
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 61 times.
169 }
48
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
61 if (closedir(directory)) {
49 ✗ return -1;
50 }
51 }
52
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 55 times.
61 if (removeThis) {
53 6 return remove(path);
54 }
55 55 return 0;
56 }
57
58 61 int main(int argc, char* argv[]) {
59 try {
60 61 bool readonly = false;
61 61 bool noperms = false;
62 61 bool forceDefaults = false;
63 61 bool dryRun = false;
64 61 std::string jurisdiction;
65
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 61 times.
123 for (int i = 1; i < argc; i++) {
66
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 58 times.
62 if (!strcmp(argv[i], "-ro")) {
67 4 readonly = true;
68
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 5 times.
58 } else if (!strcmp(argv[i], "-no-set-permissions")) {
69 53 noperms = true;
70
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 } else if (!strcmp(argv[i], "-force-defaults")) {
71 1 forceDefaults = true;
72
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
4 } else if (!strcmp(argv[i], "-dry-run") || !strcmp(argv[i], "-settings-only")) {
73 // Both spellings select the same thing: report what defaultSettings
74 // would do and change nothing. -settings-only names the part of a
75 // real run that is being simulated; -dry-run names the fact that it
76 // is a simulation.
77 2 dryRun = true;
78
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 } else if (!strncmp(argv[i], "-regime=", 8)) {
79
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 jurisdiction = argv[i] + 8;
80 }
81 }
82
83 // The setup wizard's entry point. Cape detection runs at boot, before
84 // anyone has been asked where they are, so a cape's telemetry defaults are
85 // held. Once step one of the wizard is answered this reports which of them
86 // the answer permits -- without writing anything, because nothing in that
87 // page is persisted until the user finishes it.
88
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 59 times.
61 if (dryRun) {
89
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 printf("%s\n", CapeUtils::INSTANCE.dryRunSettings(jurisdiction).c_str());
90 2 return 0;
91 }
92
93 // Clearing this is part of REGENERATING it, so it must not happen in a
94 // read-only run. The wipe used to sit above the argument loop, so
95 // `fppcapedetect -ro` deleted the live cape-info.json and then never
96 // wrote one back -- loadFiles() only copies the regenerated files out of
97 // the scratch directory when !readOnly. A box left in that state has no
98 // cape-info.json at all, which MultiSync (:714) and
99 // ManageApacheContentPolicy.sh (:92) both read.
100
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 4 times.
59 if (!readonly) {
101
1/2
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
55 remove_recursive("/home/fpp/media/tmp/", false);
102 }
103
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
59 CapeUtils::INSTANCE.initCape(readonly, forceDefaults);
104
105 // fppoled keeps a copy of the cape image on the root fs so it can put
106 // the logo up before detection has run (it snapshots media/tmp before
107 // the wipe above, at ExecStartPre). Keep that copy in step here too:
108 // an EEPROM that is blank or whose header does not match produces no
109 // cape-image.xbm at all, and the copy is what OLEDPage::readCapeImage()
110 // falls back to in exactly that case -- so without this a re-detect
111 // leaves the OLED showing the logo of a cape that is no longer there.
112 // Only on a run that regenerated media/tmp: a -ro run copies nothing
113 // out, so its "no image" means nothing about the hardware.
114
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 4 times.
51 if (!readonly) {
115 47 std::error_code ec;
116
4/6
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 45 times.
47 if (std::filesystem::is_regular_file("/home/fpp/media/tmp/cape-image.xbm")) {
117
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 std::filesystem::copy_file("/home/fpp/media/tmp/cape-image.xbm",
118 "/var/tmp/cape-image.xbm",
119 std::filesystem::copy_options::overwrite_existing, ec);
120 } else {
121
1/2
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
45 std::filesystem::remove("/var/tmp/cape-image.xbm", ec);
122 }
123 }
124
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 45 times.
51 if (!noperms) {
125 // getpwnam_r() is the thread-safe form of getpwnam().
126 char pbuf[16384];
127 struct passwd pwd;
128 6 struct passwd* pwres = nullptr;
129
4/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
9 if (getpwnam_r("fpp", &pwd, pbuf, sizeof(pbuf), &pwres) == 0 && pwres &&
130
7/12
✓ 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.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
9 std::filesystem::is_directory("/home/fpp/media/tmp/")) {
131
5/8
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 3 times.
4 for (const auto& entry : std::filesystem::directory_iterator("/home/fpp/media/tmp/")) {
132 1 chown(entry.path().c_str(), pwres->pw_uid, pwres->pw_gid);
133 3 }
134 }
135 }
136
2/4
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
53 } catch (std::exception& e) {
137 ✗ return -1;
138 ✗ }
139 51 return 0;
140 }
141