GCC Code Coverage Report


Directory: ./
File: boot/FPPINIT.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 251 321 78.2%
Functions: 14 14 100.0%
Branches: 386 878 44.0%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2024 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
13 #include <array>
14 #include "fpp-json.h"
15 #include <chrono>
16 #include <cctype>
17 #include <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <ctime>
21 #include <dirent.h>
22 #include <memory>
23 #include <pwd.h>
24 #include <sys/stat.h>
25 #include <systemd/sd-daemon.h>
26 #include <thread>
27 #include <unistd.h>
28
29 #include "FPPINIT.h"
30
31 // teeOutput() -- which sends our stdout, and that of everything we fork, to both
32 // fppd.log and the console we were started with -- now lives in common_mini.cpp
33 // so fpprtc (which links common_mini but not FPPINIT) can use it too.
34
35 2 int remove_recursive(const char* const path, bool removeThis = true) {
36 2 DIR* const directory = opendir(path);
37
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (directory) {
38 struct dirent* entry;
39
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 while ((entry = readdir(directory))) {
40
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) {
41 4 continue;
42 }
43 ✗ char filename[strlen(path) + strlen(entry->d_name) + 2];
44 ✗ sprintf(filename, "%s/%s", path, entry->d_name);
45 ✗ if (entry->d_type == DT_DIR) {
46 ✗ if (remove_recursive(filename)) {
47 ✗ closedir(directory);
48 ✗ return -1;
49 }
50 } else {
51 ✗ if (remove(filename)) {
52 ✗ closedir(directory);
53 ✗ return -1;
54 }
55 }
56
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 }
57
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (closedir(directory)) {
58 ✗ return -1;
59 }
60 }
61
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (removeThis) {
62 ✗ return remove(path);
63 }
64 2 return 0;
65 }
66
67 827 void exec(const std::string& cmd) {
68 std::array<char, 128> buffer;
69 struct PipeCloser {
70 827 void operator()(FILE* f) const {
71
1/2
✓ Branch 0 taken 827 times.
✗ Branch 1 not taken.
827 if (f)
72 827 pclose(f);
73 827 }
74 };
75
1/2
✓ Branch 0 taken 827 times.
✗ Branch 1 not taken.
827 std::unique_ptr<FILE, PipeCloser> pipe(popen(cmd.c_str(), "r"));
76
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 827 times.
827 if (!pipe) {
77 ✗ throw std::runtime_error("popen() failed!");
78 }
79
3/4
✓ Branch 0 taken 839 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 827 times.
2505 while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
80
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 printf("%s", buffer.data());
81 }
82 827 }
83 42 bool isShellSafeWord(const std::string& s) {
84
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 if (s.empty()) {
85 ✗ return false;
86 }
87
2/2
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 42 times.
184 for (char c : s) {
88
6/12
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 140 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
142 if (!isalnum((unsigned char)c) && c != '.' && c != '_' && c != '/' && c != '+' && c != '-') {
89 ✗ return false;
90 }
91 }
92 42 return true;
93 }
94 105 int execbg(const std::string& cmd) {
95 105 return system(cmd.c_str());
96 }
97 2300 std::string execAndReturn(const std::string& cmd) {
98 std::array<char, 128> buffer;
99 struct PipeCloser {
100 2300 void operator()(FILE* f) const {
101
1/2
✓ Branch 0 taken 2300 times.
✗ Branch 1 not taken.
2300 if (f)
102 2300 pclose(f);
103 2300 }
104 };
105
1/2
✓ Branch 0 taken 2300 times.
✗ Branch 1 not taken.
2300 std::unique_ptr<FILE, PipeCloser> pipe(popen(cmd.c_str(), "r"));
106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2300 times.
2300 if (!pipe) {
107 ✗ throw std::runtime_error("popen() failed!");
108 }
109 2300 std::string ret;
110
3/4
✓ Branch 0 taken 3692 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1392 times.
✓ Branch 3 taken 2300 times.
9954 while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
111
2/4
✓ Branch 0 taken 1392 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1392 times.
✗ Branch 3 not taken.
3906 ret += std::string(buffer.data());
112 }
113 2300 return ret;
114 2300 }
115
116 193 bool LoadJsonFromString(const std::string& str, Json::Value& root) {
117
1/2
✓ Branch 0 taken 193 times.
✗ Branch 1 not taken.
193 Json::CharReaderBuilder builder;
118
1/2
✓ Branch 0 taken 193 times.
✗ Branch 1 not taken.
193 Json::CharReader* reader = builder.newCharReader();
119 193 std::string errors;
120
3/6
✓ Branch 0 taken 193 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 193 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 193 times.
✗ Branch 5 not taken.
579 builder["collectComments"] = false;
121
2/2
✓ Branch 0 taken 191 times.
✓ Branch 1 taken 2 times.
193 bool success = reader->parse(str.c_str(), str.c_str() + str.size(), &root, &errors);
122
1/2
✓ Branch 0 taken 191 times.
✗ Branch 1 not taken.
191 delete reader;
123
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 189 times.
191 if (!success) {
124
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 Json::Value empty;
125
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 root = empty;
126 2 return false;
127 2 }
128 189 return true;
129 195 }
130
131 // A file that parses cleanly but holds the wrong kind of root - an array where
132 // an object is expected, or the reverse - is a corrupt config, not usable data.
133 // Every jsoncpp accessor a caller reaches for next (operator[], isMember(),
134 // getMemberNames(), get()) throws Json::LogicError on that mismatch, and
135 // fppinit has no systemd Restart=, so a bad config file here doesn't restart
136 // a daemon, it kills boot. This mirrors the JsonRoot check on FPP's own
137 // loader in common.cpp; it is duplicated rather than shared because fppinit
138 // does not pull in common.h.
139 177 bool LoadJsonFromString(const std::string& str, Json::Value& root, JsonRoot expected) {
140
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 168 times.
177 Json::ValueType expectedType = (expected == JsonRoot::Array) ? Json::arrayValue : Json::objectValue;
141
142
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 173 times.
177 if (!LoadJsonFromString(str, root)) {
143
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 root = Json::Value(expectedType);
144 2 return false;
145 }
146
147 // A null root is not a mismatch - jsoncpp treats null as an empty value of
148 // whatever type it is first used as and never throws on it.
149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 173 times.
173 if (root.isNull()) {
150 ✗ root = Json::Value(expectedType);
151 ✗ return true;
152 }
153
154
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 172 times.
173 if (root.type() != expectedType) {
155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 printf("JSON string has the wrong root type, expected %s - treating as empty\n",
156 (expectedType == Json::arrayValue) ? "array" : "object");
157
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 root = Json::Value(expectedType);
158 1 return false;
159 }
160
161 172 return true;
162 }
163 126 std::string SaveJsonToString(const Json::Value& root) {
164
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
126 Json::StreamWriterBuilder wbuilder;
165
3/6
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 126 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 126 times.
✗ Branch 5 not taken.
378 wbuilder["indentation"] = "\t";
166
167
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
126 std::string result = Json::writeString(wbuilder, root);
168
169 126 return result;
170 126 }
171
172 40 void modprobe(const char* mod) {
173
2/4
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 40 times.
✗ Branch 3 not taken.
40 std::string cmd = std::string("/usr/sbin/modprobe ") + mod;
174
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
40 exec(cmd);
175 40 }
176
177 142 int main(int argc, char* argv[]) {
178 // Everything fppinit runs, it runs to parse: aplay, arecord, amixer and
179 // the network tools all translate their output, and systemd hands every
180 // service the system locale. Under fr_FR "aplay -l" lists "carte 0:",
181 // and the audio setup found no sound card at all. C.UTF-8 keeps UTF-8
182 // file names intact and has no translations.
183 142 setenv("LC_ALL", "C.UTF-8", 1);
184
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 std::string networkSetupMut = FPP_MEDIA_DIR + "/tmp/networkSetup";
185
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 std::string action = "start";
186
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 if (argc > 1) {
187
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 action = argv[1];
188 }
189 // Both actions now log to the shared fppd.log (was fpp_init.log /
190 // fpp_boot.log); the facility keeps them apart within the file. Same
191 // directory as before, so the retry below still covers the same failure --
192 // media/logs not yet mounted or writable on a first/degraded boot.
193
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 std::string logFile = FPP_MEDIA_DIR + "/logs/fppd.log";
194
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 FILE* f = fopen(logFile.c_str(), "a+");
195 142 int sleepCount = 0;
196
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 142 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
142 while (!f && sleepCount < 30) {
197 ✗ printf("Could not open log\n");
198 ✗ sleepCount++;
199 ✗ std::this_thread::sleep_for(std::chrono::milliseconds(100));
200 ✗ f = fopen(logFile.c_str(), "a+");
201 }
202
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 if (f) {
203 // This fopen is the first thing to touch fppd.log on a box where it does
204 // not exist -- fppinit bootPre is fppd.service's ExecStartPre, so it gets
205 // there before fppd itself -- and we run as root, so the file would be
206 // left root:root 0644 in an fpp-owned directory. fppd.log is no longer
207 // fppd's alone: the fppd_start/stop/restart scripts append to it, and so
208 // do the PHP breadcrumbs (backup, mp3gain) which run as Apache/fpp. Their
209 // writes are error-suppressed best-effort, so they would fail SILENTLY.
210 // Whoever creates the file owns making it writable by fpp; here, that is
211 // us. (log.cpp does the same for the case where fppd creates it first,
212 // and logrotate's `create 0664 fpp fpp` covers the rotation case.)
213
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 struct passwd* pw = getpwnam("fpp");
214
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 78 times.
142 if (pw) {
215
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 if (fchown(fileno(f), pw->pw_uid, pw->pw_gid) == 0) {
216 64 fchmod(fileno(f), 0664);
217 }
218 }
219
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 fclose(f);
220 // Every fppinit invocation that produces output goes to fppd.log. The
221 // facility names the phase (fppinit is the program field, so the phase is
222 // the only thing distinguishing one invocation from another); they are
223 // deliberately NOT fppd facilities from log.h -- fppinit is not fppd.
224 //
225 // bootPre/bootPost are fppd.service's ExecStartPre/ExecStartPost. They
226 // were the last FPP output still escaping to journald -- the boot banners,
227 // cape/eeprom detection and sysctl settings, which teeOutput's dup2 picks
228 // up because they come from processes fppinit forks. That output is real
229 // diagnostic content and journald here is RAM-only (/run/log/journal), so
230 // it died at reboot and never reached a Support Zip.
231
3/4
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 139 times.
142 if (action == "start") {
232
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.
13 teeOutput(logFile, "fppinit", "Startup", getpid());
233
3/4
✓ Branch 0 taken 139 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 116 times.
139 } else if (action == "postNetwork") {
234
3/6
✓ 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.
115 teeOutput(logFile, "fppinit", "Boot", getpid());
235
3/4
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 113 times.
116 } else if (action == "bootPre") {
236
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.
15 teeOutput(logFile, "fppinit", "BootPre", getpid());
237
2/4
✓ Branch 0 taken 113 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 113 times.
113 } else if (action == "bootPost") {
238 ✗ teeOutput(logFile, "fppinit", "BootPost", getpid());
239
11/18
✓ Branch 0 taken 113 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 111 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 111 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 111 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 111 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 111 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 111 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 111 times.
✓ Branch 16 taken 2 times.
✓ Branch 17 taken 111 times.
113 } else if (action == "runPreStartScripts" || action == "runPostStartScripts" || action == "runPreStopScripts" || action == "runPostStopScripts") {
240 // Standalone equivalents of the runScripts() calls inside start/
241 // bootPre/bootPost (already teed there); not currently invoked by
242 // anything shipped in this repo, but if a plugin or admin script
243 // ever calls `fppinit runPreStartScripts` etc. directly, its output
244 // - and now, which plugin/UserCallbackHook scripts it found and ran -
245 // should land in fppd.log rather than nowhere.
246
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.
10 teeOutput(logFile, "fppinit", "Scripts", getpid());
247
10/14
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 89 times.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 89 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 87 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 87 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 87 times.
✓ Branch 12 taken 24 times.
✓ Branch 13 taken 87 times.
111 } else if (action == "setupNetwork" || action == "checkForTether" || action == "maybeRemoveTether") {
248 // These drive AP<->client WiFi transitions (the interactive "Restart
249 // Network" apply, and the networkd-dispatcher-triggered tether
250 // watchdog/removal). Without this, their output only reaches
251 // journald (setupNetwork's is captured by the PHP caller and shown
252 // in the browser, but never persisted) or the networkd-dispatcher
253 // process's own syslog lines - invisible to fppd.log either way,
254 // even though this is exactly the sequence a WiFi-setup bug report
255 // needs to reconstruct.
256
3/6
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
120 teeOutput(logFile, "fppinit", "Network", getpid());
257
3/4
✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 59 times.
✓ Branch 3 taken 28 times.
87 } else if (action == "setupAudio") {
258 // Backgrounded by settings.php with its stdout/stderr explicitly sent
259 // to /dev/null so the HTTP response returns immediately - teeOutput's
260 // dup2 replaces that fd before we print anything, so this capture
261 // still works even though the parent shell redirected us away.
262
3/6
✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 59 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 59 times.
✗ Branch 5 not taken.
260 teeOutput(logFile, "fppinit", "Audio", getpid());
263
16/26
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 25 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 25 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 25 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 25 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 25 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 22 times.
✓ Branch 17 taken 3 times.
✓ Branch 18 taken 22 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 21 times.
✓ Branch 21 taken 1 times.
✓ Branch 22 taken 21 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 17 times.
✓ Branch 25 taken 4 times.
53 } else if (action == "configureBBB" || action == "applyThermal" || action == "resetThermal" || action == "installKiosk" || action == "setupPiRTC" || action == "setupHDMICEC" || action == "setupWifiAntenna" ||
264
7/12
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 13 times.
✓ 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.
17 action == "setupChannelOutputs" || action == "checkConfigMigrations" || action == "detectCape" ||
265
11/16
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 3 times.
✓ Branch 14 taken 25 times.
✓ Branch 15 taken 3 times.
53 action == "setupTimezone" || action == "checkHostName" || action == "handleBootActions") {
266 // Same gap as setupNetwork: invoked directly via PHP exec(), output
267 // otherwise lives only in that request's $output/HTTP response.
268
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.
101 teeOutput(logFile, "fppinit", "Config", getpid());
269
5/10
✓ 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 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
3 } else if (action == "announceIP" || action == "resizeRootFS") {
270 // One-shot systemd services (fpp-announce-ip / fpp-expand-rootfs);
271 // same RAM-only-journald loss as bootPre/bootPost before they were
272 // added above.
273 ✗ teeOutput(logFile, "fppinit", "Boot", getpid());
274
3/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
3 } else if (action == "reboot") {
275 // fpp-reboot.service - capture why/what before the box actually goes
276 // down, so it's the last thing in fppd.log rather than nothing.
277
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
4 teeOutput(logFile, "fppinit", "Reboot", getpid());
278 }
279 } else {
280 ✗ printf("Could not too output to log. Directory might not be writable or maybe full.\n");
281 }
282
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 printf("------------------------------\nRunning FPPINIT %s", action.c_str());
283
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 142 times.
144 for (int x = 2; x < argc; x++) {
284
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 printf(" %s", argv[x]);
285 }
286
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 printf("\n");
287
3/4
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 139 times.
142 if (action == "start") {
288
3/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
3 if (!FileExists("/etc/fpp/container")) {
289 #ifdef CAPEDETECT
290
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 printf("FPP - Cleaning tmp\n");
291
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 remove_recursive((FPP_MEDIA_DIR + "/tmp/").c_str(), false);
292 #endif
293 }
294
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 createDirectories();
295
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 printf("FPP - Directories created\n");
296
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 bool needReboot = checkUnpartitionedSpace();
297
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 checkSSHKeys();
298
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 handleBootPartition();
299
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 checkHostName();
300
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 checkFSTAB();
301
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setupApache();
302
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 DetectCape();
303
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setupTimezone();
304 // After DetectCape() above, so a cape shipping DisablePiRTC in its
305 // defaultSettings is honoured on the boot it is first detected on.
306
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setupPiRTCConfig();
307
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setupHDMICECConfig();
308
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setupWifiAntennaConfig();
309
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setup16KPageKernel();
310
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 int reboot = getRawSettingInt("rebootFlag", 0);
311
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if (reboot && !needReboot) {
312 ✗ printf("FPP - Clearing reboot flags\n");
313 ✗ setRawSetting("rebootFlag", "0");
314 }
315
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 handleBootActions();
316
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 runScripts("boot", true);
317
7/18
✓ 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 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
6 int skipNetwork = FileExists("/etc/fpp/desktop") || getRawSettingInt("SkipNetworkReset", 0);
318
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (!skipNetwork) {
319 // Respect the same "FPP doesn't own networking here" gate as
320 // setupNetwork() below -- this touches the same
321 // systemd-networkd-owned lease state, so it must not run on
322 // desktop/dev builds or when the admin has explicitly opted out
323 // via SkipNetworkReset.
324
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 consumePendingDhcpLeaseReset();
325
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setupNetwork();
326 }
327
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 configureBBB();
328
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 applyThermalSettings(true);
329 // Probe each fan's tachometer (forces it on briefly) so fppd can hide
330 // the RPM for a fan it can't detect. Must run before fppd starts.
331
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 probeFanPresence();
332
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setupChannelOutputs();
333
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setupKiosk();
334
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 printf("Setting file ownership\n");
335
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setFileOwnership();
336
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.
7 PutFileContents(FPP_MEDIA_DIR + "/tmp/cape_detect_done", "1");
337
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 checkInstallKiosk();
338
339
3/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
3 if (!FileExists("/etc/fpp/container")) {
340 // Create boot delay flag file early if boot delay is configured
341 // so UI can show warning immediately when Apache starts
342
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 int bootDelaySetting = getRawSettingInt("bootDelay", -1);
343
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (bootDelaySetting != 0) {
344 // Store start time and duration/mode for UI countdown
345 2 time_t startTime = time(nullptr);
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (bootDelaySetting > 0) {
347 ✗ std::string flagContent = std::to_string(startTime) + "," + std::to_string(bootDelaySetting);
348 ✗ PutFileContents(FPP_MEDIA_DIR + "/tmp/boot_delay", flagContent);
349
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 } else if (bootDelaySetting == -1) {
350
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 std::string flagContent = std::to_string(startTime) + ",auto";
351
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 PutFileContents(FPP_MEDIA_DIR + "/tmp/boot_delay", flagContent);
352 2 }
353 }
354 } else {
355 // Ensure no boot delay flag file exists. No delay in docker.
356
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 unlink((FPP_MEDIA_DIR + "/tmp/boot_delay").c_str());
357 }
358
359 // Notify systemd that initialization is complete
360
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 sd_notify(0, "READY=1\nSTATUS=FPP initialization complete");
361
3/4
✓ Branch 0 taken 139 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 116 times.
139 } else if (action == "postNetwork") {
362
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 handleBootDelay();
363 // turn off blinking cursor + clean up kiosk leftovers (quick, independent)
364
3/6
✓ 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.
69 PutFileContents("/sys/class/graphics/fbcon/cursor_blink", "0");
365
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 cleanupChromiumFiles();
366
367 // Audio setup and network bring-up no longer depend on each other (the
368 // flite IP announcement moved out to fpp-announce-ip.service), so run
369 // them concurrently. The network thread spends most of its time *waiting*
370 // (DHCP lease, NTP sync), yielding the CPU, while setupAudio does its
371 // CPU/IO work -- so they overlap well even on a single-core board and
372 // shorten postNetwork.
373 //
374 // Thread-safety: the only shared mutable state is /home/fpp/media/settings.
375 // setupAudio() is the SOLE writer; the network functions only read it.
376 // get/setRawSetting go through GetFileContents/PutFileContents, which take
377 // an advisory flock (LOCK_SH / LOCK_EX), so a read never observes a partial
378 // write. Keep the network path settings-read-only to preserve this. Each
379 // thread is wrapped so an exception can't escape and std::terminate the
380 // whole boot.
381 ✗ std::thread audioThread([]() {
382 try {
383
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 setupAudio();
384 // The PipeWire services ship disabled and are NOT started at
385 // sound.target -- starting them only now, after setupAudio has
386 // written/validated the config, means PipeWire reads the correct
387 // graph on its first (and only) start instead of starting empty
388 // at boot and needing a restart. Backgrounded so the audio thread
389 // doesn't block on the service bring-up. fpp-pipewire is pulled in
390 // by the other two's Requires=, but list it for clarity.
391
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
46 execbg("systemctl start fpp-pipewire.service fpp-wireplumber.service fpp-pipewire-pulse.service &");
392 ✗ } catch (const std::exception& e) {
393 ✗ printf("FPP - setupAudio failed: %s\n", e.what());
394 ✗ }
395
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
46 });
396 ✗ std::thread networkThread([]() {
397 try {
398
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 removeDummyInterface();
399
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 checkWLANInterface();
400
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 removeDummyInterface();
401 // Wait for an actual IP, not just link: fppd starts right after
402 // this service and needs the address in place (socket binds,
403 // MultiSync), and the time-sync wait below may need the network
404 // too. The boot path may also recover a dead USB adapter.
405
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 waitForInterfacesUp(100, true);
406 // A wedged USB adapter has queued a reboot. Everything below is
407 // setup for a boot that is about to end, and this service is on
408 // fppd's dependency chain -- overrunning its start timeout here
409 // is what fails fppd, so finish immediately instead.
410
3/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 2 times.
23 if (!usbWedgeRebootPending()) {
411 // Interfaces exist and are associated by here, which is what iw
412 // needs; doing it earlier would miss an adapter that enumerated late.
413
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 disableWLANPowerManagement();
414 // Time sync wait happens AFTER interfaces are up so NTP can sync
415
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 handleTimeSyncWait();
416
2/4
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
21 if (!FileExists("/etc/fpp/desktop")) {
417
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 maybeEnableTethering();
418
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 detectNetworkModules();
419 }
420 }
421 ✗ } catch (const std::exception& e) {
422 ✗ printf("FPP - network setup failed: %s\n", e.what());
423 ✗ }
424
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
46 });
425 // setupTimezone and swap setup depend on neither thread, so run them here
426 // -- concurrent with the network thread's DHCP/NTP wait and the audio
427 // thread -- instead of serially after the joins. setupTimezone only READS
428 // settings, which is flock-safe against setupAudio's writes.
429
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 setupTimezone(); // this may not have worked in the init phase, try again
430
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 startZRAMSwap();
431
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 startDiskSwap();
432 // Fan/thermal drivers (e.g. pwm-fan) may be kernel modules that probe
433 // after the early "start" phase ran, so re-apply the trip settings here.
434 // Zones already snapshotted at "start" are not re-captured; one that
435 // only appeared now is still at its device tree values.
436
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 applyThermalSettings(true);
437
438
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 audioThread.join();
439
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 networkThread.join();
440
441 // checkConfigMigrations doesn't need the network, but runs here anyway,
442 // right beside checkInstallPackages -- both are gated on the same
443 // markers, so keeping them together keeps that shared precondition
444 // visible in one place.
445 //
446 // /fppos_upgraded is touched by an OS reflash (upgradeOS-part2.sh)
447 // and /fpp_restored by a restore: www/backup.php after any JSON
448 // restore area, www/copystorage.php after a File Copy restore of
449 // Configuration, Plugins or EEPROM. A restore gets the packages and
450 // migrations, not the reflash's plugin handling.
451
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 checkConfigMigrations();
452
453 // Both of these must run after a join:
454 // - checkInstallPackages may apt-install user packages after an OS
455 // upgrade or a restore (see the marker note above), so
456 // it needs the network up (network thread joined).
457 // - setFileOwnership chowns /home/fpp/media, where setupAudio writes its
458 // PipeWire config as root, so it must follow the audio thread -- run it
459 // last so it also catches anything checkInstallPackages dropped there.
460
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 checkInstallPackages();
461
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 setFileOwnership();
462 // Notify systemd that post-network setup is complete
463
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 sd_notify(0, "READY=1\nSTATUS=FPP post-network setup complete");
464
3/4
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 113 times.
139 } else if (action == "bootPre") {
465
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 int restart = getRawSettingInt("restartFlag", 0);
466
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (restart) {
467 ✗ printf("FPP - Clearing restart flags\n");
468 ✗ setRawSetting("restartFlag", "0");
469 }
470
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 migrateLegacyFppMode();
471
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 migrateRemoteCommands();
472
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setupChannelOutputs();
473
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
6 runScripts("preStart", true);
474
2/4
✓ Branch 0 taken 113 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 113 times.
113 } else if (action == "bootPost") {
475 ✗ std::this_thread::sleep_for(std::chrono::milliseconds(100));
476 ✗ runScripts("postStart", false);
477
3/4
✓ Branch 0 taken 113 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 111 times.
113 } else if (action == "runPreStartScripts") {
478
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 runScripts("preStart", true);
479
2/4
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 111 times.
111 } else if (action == "runPostStartScripts") {
480 ✗ runScripts("postStart", false);
481
2/4
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 111 times.
111 } else if (action == "runPreStopScripts") {
482 ✗ runScripts("preStop", true);
483
2/4
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 111 times.
111 } else if (action == "runPostStopScripts") {
484 ✗ runScripts("postStop", false);
485
3/4
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 59 times.
✓ Branch 3 taken 52 times.
111 } else if (action == "setupAudio") {
486
1/2
✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
59 setupAudio();
487
2/4
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
52 } else if (action == "announceIP") {
488 // Run from fpp-announce-ip.service, after fpp_postnetwork + PipeWire, so
489 // the flite synthesis doesn't slow the tail of postNetwork / fppd start.
490 ✗ announceIPAddresses();
491
3/4
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 49 times.
52 } else if (action == "configureBBB") {
492
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 configureBBB();
493
2/4
✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
49 } else if (action == "applyThermal") {
494 ✗ applyThermalSettings();
495
2/4
✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
49 } else if (action == "resetThermal") {
496 ✗ resetThermalSettings();
497
2/4
✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
49 } else if (action == "installKiosk") {
498 ✗ installKiosk();
499
3/4
✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 46 times.
49 } else if (action == "setupPiRTC") {
500 // The UI's "reboot": 1 on DisablePiRTC prompts the user; don't reboot here.
501
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 setupPiRTCConfig(false);
502
3/4
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 45 times.
46 } else if (action == "setupHDMICEC") {
503 // The UI's "reboot": 1 on DisableHDMICECInit prompts the user; don't reboot here.
504
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 setupHDMICECConfig(false);
505
3/4
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 41 times.
45 } else if (action == "setupWifiAntenna") {
506 // The UI's "reboot": 1 on WifiAntenna prompts the user; don't reboot here.
507
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 setupWifiAntennaConfig(false);
508
3/4
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 39 times.
41 } else if (action == "setup16KPageKernel") {
509 // The UI's "reboot": 1 on Pi16KPageKernel prompts the user; don't reboot here.
510
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 setup16KPageKernel(false);
511
3/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 17 times.
39 } else if (action == "setupNetwork") {
512
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
22 PutFileContents(networkSetupMut, "1");
513
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 setupNetwork(true);
514
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 waitForInterfacesUp(70);
515
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 detectNetworkModules();
516
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 maybeEnableTethering();
517 22 unlink(networkSetupMut.c_str());
518
3/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 15 times.
17 } else if (action == "checkForTether") {
519
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (!FileExists(networkSetupMut)) {
520
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 std::string a = execAndReturn("systemctl is-active fpp_postnetwork");
521
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 TrimWhiteSpace(a);
522
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 if (a.starts_with("active") && argc >= 3) {
523
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 std::string iface = argv[2];
524
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (iface.starts_with("wlan")) {
525
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 waitForInterfacesUp(20);
526
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 maybeEnableTethering();
527
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 detectNetworkModules();
528 }
529 2 }
530 2 }
531
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 } else if (action == "maybeRemoveTether") {
532 ✗ int te = getRawSettingInt("EnableTethering", 0);
533 ✗ if (!FileExists(networkSetupMut) && (te != 1)) {
534 ✗ std::string e = execAndReturn("systemctl is-enabled hostapd");
535 ✗ std::string a = execAndReturn("systemctl is-active hostapd");
536 ✗ TrimWhiteSpace(e);
537 ✗ TrimWhiteSpace(a);
538 ✗ if (a.starts_with("active") || e.starts_with("enabled")) {
539 ✗ std::string iface = argv[2];
540 ✗ if (FindTetherWIFIAdapater() != iface && !iface.starts_with("usb") && !iface.starts_with("lo") && waitForInterfacesUp(10)) {
541 ✗ exec("rm -f /etc/systemd/network/10-" + FindTetherWIFIAdapater() + ".network");
542 ✗ exec("rm -f " + FPP_MEDIA_DIR + "/tmp/wifi-*.ascii");
543 ✗ exec("systemctl stop hostapd.service");
544 ✗ exec("systemctl disable hostapd.service");
545 ✗ exec("systemctl reload-or-restart systemd-networkd.service");
546 }
547 ✗ }
548 ✗ }
549
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 } else if (action == "detectNetworkModules") {
550 ✗ detectNetworkModules();
551
3/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 2 times.
15 } else if (action == "setupChannelOutputs") {
552 // The single steps below normally run inside start/bootPre; as actions
553 // of their own they can be run, and tested, one at a time
554 // (FPP_MEDIA_DIR points them at another media directory).
555
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 setupChannelOutputs();
556
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 } else if (action == "checkConfigMigrations") {
557 ✗ checkConfigMigrations();
558
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 } else if (action == "detectCape") {
559 ✗ DetectCape();
560
3/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 } else if (action == "setupTimezone") {
561
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 setupTimezone();
562
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 } else if (action == "checkHostName") {
563 ✗ checkHostName();
564
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 } else if (action == "handleBootActions") {
565 ✗ handleBootActions();
566
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 } else if (action == "reboot") {
567
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 handleRebootActions();
568 ✗ } else if (action == "resizeRootFS") {
569 ✗ resizeRootFS();
570 }
571
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 printf("------------------------------\n");
572 142 return 0;
573 142 }
574