5#include <QtCore/qjsondocument.h>
6#include <QtCore/private/qohoslogger_p.h>
15#include <sys/socket.h>
23using namespace std::string_literals;
32const auto appTempDir =
"/data/storage/el2/base/temp";
34constexpr unsigned qChildSetupDataSendTimeoutSecs = 5;
36constexpr auto qChildSetupDataSendRetryDelay = std::chrono::milliseconds(50);
38constexpr std::size_t qChildSetupMaxSetupDataSize = 64 * 1024;
40std::string getSetupDataExchangeDirPath()
42 return appTempDir +
"/qohos-child-setup-data"s;
45std::string getChildSetupDataPath(
int childPid)
47 return getSetupDataExchangeDirPath() +
"/"s + std::to_string(childPid);
50bool tryMakeSetupDataExchangeDirIfNeeded()
52 auto setupDataExchangeDirPath = getSetupDataExchangeDirPath();
54 if (::mkdir(setupDataExchangeDirPath.c_str(), 0700) != 0 && errno != EEXIST) {
56 "%s: error creating 'child setup data' directory: %s",
57 Q_FUNC_INFO,
std::strerror(errno));
61 struct ::stat statBuf;
62 if (::stat(setupDataExchangeDirPath.c_str(), &statBuf) != 0) {
64 "%s: stat() failed for 'child setup data' directory: %s",
65 Q_FUNC_INFO,
std::strerror(errno));
69 if (!S_ISDIR(statBuf.st_mode)) {
71 "%s: non-directory found at 'child setup data' directory path", Q_FUNC_INFO);
78bool tryWriteChildSetupDataFile(
int childPid,
const std::string &setupDataStr)
80 auto childSetupDataPath = getChildSetupDataPath(childPid);
81 auto tmpChildSetupDataPath = childSetupDataPath +
".tmp"s;
83 if (!tryMakeSetupDataExchangeDirIfNeeded())
86 (
void) ::unlink(childSetupDataPath.c_str());
87 (
void) ::unlink(tmpChildSetupDataPath.c_str());
89 auto openRes = ::open(tmpChildSetupDataPath.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0600);
92 "%s: error opening 'child setup data' file '%s' for write: %s",
93 Q_FUNC_INFO, tmpChildSetupDataPath.c_str(),
std::strerror(errno));
98 auto writeRes = ::write(fileFd, setupDataStr.data(), setupDataStr.size());
99 int writeErrno = errno;
101 (
void) ::close(fileFd);
104 (
void) ::unlink(tmpChildSetupDataPath.c_str());
106 "%s: error writing to 'child setup data' file '%s': %s",
107 Q_FUNC_INFO, tmpChildSetupDataPath.c_str(),
std::strerror(writeErrno));
110 auto writtenSize =
static_cast<
std::size_t>(writeRes);
111 if (writtenSize != setupDataStr.size()) {
112 (
void) ::unlink(tmpChildSetupDataPath.c_str());
114 "%s: incomplete write to 'child setup data' file: %zu/%zu",
115 Q_FUNC_INFO, writtenSize, setupDataStr.size());
119 if (::rename(tmpChildSetupDataPath.c_str(), childSetupDataPath.c_str()) != 0) {
121 "%s: error renaming 'child setup data' file '%s': %s",
122 Q_FUNC_INFO, childSetupDataPath.c_str(),
std::strerror(errno));
130std::optional<
std::string> tryReadChildSetupDataFile(
int childPid)
132 auto childSetupDataPath = getChildSetupDataPath(childPid);
134 auto openRes = ::open(childSetupDataPath.c_str(), O_RDONLY);
135 if (openRes < 0 && errno == ENOENT)
136 return std::string();
139 "%s: error opening 'child setup data' file '%s' for read: %s",
140 Q_FUNC_INFO, childSetupDataPath.c_str(),
std::strerror(errno));
143 int fileFd = openRes;
145 char readBuffer[qChildSetupMaxSetupDataSize + 1];
146 auto readRes = ::read(fileFd, readBuffer,
sizeof(readBuffer));
147 int readErrno = errno;
149 (
void) ::close(fileFd);
153 "%s: error reading 'child setup data' file '%s': %s",
154 Q_FUNC_INFO, childSetupDataPath.c_str(),
std::strerror(readErrno));
157 auto readSize =
static_cast<
std::size_t>(readRes);
158 if (readSize > qChildSetupMaxSetupDataSize) {
160 "%s: received 'child setup data' file '%s' is too big",
161 Q_FUNC_INFO, childSetupDataPath.c_str());
166 "%s: received 'child setup data' file '%s' is empty",
167 Q_FUNC_INFO, childSetupDataPath.c_str());
171 return std::string(readBuffer, readSize);
174bool checkIfChildSetupDataFileExists(
int childPid)
176 return ::access(getChildSetupDataPath(childPid).c_str(), F_OK) == 0;
179void removeChildSetupDataFileIfExists(
int childPid)
181 (
void) ::unlink(getChildSetupDataPath(childPid).c_str());
185std::optional<
std::string> tryWaitForChildSetupData(
int childPid)
187 namespace ch =
std::chrono;
189 auto timeoutEnd = ch::steady_clock::now() + ch::seconds(qChildSetupDataSendTimeoutSecs);
191 auto optSetupDataStr = tryReadChildSetupDataFile(childPid);
192 if (!optSetupDataStr)
194 if (!optSetupDataStr->empty()) {
195 removeChildSetupDataFileIfExists(childPid);
196 return optSetupDataStr;
198 std::this_thread::sleep_for(qChildSetupDataSendRetryDelay);
199 }
while (ch::steady_clock::now() < timeoutEnd);
201 qOhosPrintfError(
"%s: timeout waiting for 'child setup data' file", Q_FUNC_INFO);
209 auto optSetupDataStr = tryWaitForChildSetupData(::getpid());
210 if (!optSetupDataStr)
211 return QNapi::makeObject(env);
213 QNapi::Object global = env.Global();
215 return global.eval<QNapi::Object>(
"JSON.parse(*)", {optSetupDataStr.value()});
216 }
catch (
const Napi::Error &e) {
217 qOhosPrintfError(
"%s: subprocess setup data has illegal format: %s", Q_FUNC_INFO, e.what());
218 return QNapi::makeObject(env);
224 namespace ch =
std::chrono;
226 auto senderThread = std::thread(
227 [childPid, setupData]() {
228 auto setupDataStr = QJsonDocument(setupData).toJson(QJsonDocument::Compact).toStdString();
230 if (!tryWriteChildSetupDataFile(childPid, setupDataStr))
233 auto startTime = ch::steady_clock::now();
234 auto timeoutEnd = startTime + ch::seconds(qChildSetupDataSendTimeoutSecs);
236 std::this_thread::sleep_for(qChildSetupDataSendRetryDelay);
237 }
while (checkIfChildSetupDataFileExists(childPid) && ch::steady_clock::now() < timeoutEnd);
239 if (checkIfChildSetupDataFileExists(childPid)) {
240 removeChildSetupDataFileIfExists(childPid);
241 qOhosPrintfError(
"%s: failed to send setup data to child %d", Q_FUNC_INFO, childPid);
244 senderThread.detach();
QNapi::Object readChildProcessSetupData(Napi::Env env)
void sendChildProcessSetupData(int childPid, QJsonObject setupData)