5#include <QtCore/qjsondocument.h>
6#include <QtCore/private/qohoslogger_p.h>
15#include <sys/socket.h>
20#include <system_error>
24using namespace std::string_literals;
33const auto appTempDir =
"/data/storage/el2/base/temp";
35constexpr unsigned qChildSetupDataSendTimeoutSecs = 5;
37constexpr auto qChildSetupDataSendRetryDelay = std::chrono::milliseconds(50);
39constexpr std::size_t qChildSetupMaxSetupDataSize = 64 * 1024;
41std::system_error makeSystemErrorFromErrno(
const std::string &message,
int errnoValue)
43 return std::system_error(errnoValue,
std::generic_category(), message);
46std::string getSetupDataExchangeDirPath()
48 return appTempDir +
"/qohos-child-setup-data"s;
51std::string getChildSetupDataPath(
int childPid)
53 return getSetupDataExchangeDirPath() +
"/"s + std::to_string(childPid);
56void makeSetupDataExchangeDirIfNeeded()
58 auto setupDataExchangeDirPath = getSetupDataExchangeDirPath();
60 if (::mkdir(setupDataExchangeDirPath.c_str(), 0700) != 0 && errno != EEXIST) {
61 throw makeSystemErrorFromErrno(
62 "error creating 'child setup data' directory", errno);
65 struct ::stat statBuf;
66 if (::stat(setupDataExchangeDirPath.c_str(), &statBuf) != 0) {
67 throw makeSystemErrorFromErrno(
68 "stat() failed for 'child setup data' directory", errno);
71 if (!S_ISDIR(statBuf.st_mode))
72 throw std::runtime_error(
"non-directory found at 'child setup data' directory path");
75void writeChildSetupDataFile(
int childPid,
const std::string &setupDataStr)
77 auto childSetupDataPath = getChildSetupDataPath(childPid);
78 auto tmpChildSetupDataPath = childSetupDataPath +
".tmp"s;
80 makeSetupDataExchangeDirIfNeeded();
82 (
void) ::unlink(childSetupDataPath.c_str());
83 (
void) ::unlink(tmpChildSetupDataPath.c_str());
85 auto openRes = ::open(tmpChildSetupDataPath.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0600);
87 throw makeSystemErrorFromErrno(
88 "error opening 'child setup data' file '"s + tmpChildSetupDataPath +
"' for write"s, errno);
92 auto writeRes = ::write(fileFd, setupDataStr.data(), setupDataStr.size());
93 int writeErrno = errno;
95 (
void) ::close(fileFd);
98 (
void) ::unlink(tmpChildSetupDataPath.c_str());
99 throw makeSystemErrorFromErrno(
100 "error writing to 'child setup data' file '"s + tmpChildSetupDataPath +
"'"s, writeErrno);
102 auto writtenSize =
static_cast<
std::size_t>(writeRes);
103 if (writtenSize != setupDataStr.size()) {
104 (
void) ::unlink(tmpChildSetupDataPath.c_str());
105 throw std::runtime_error(
106 "incomplete write to 'child setup data' file: "s
107 + std::to_string(writtenSize) +
"/"s + std::to_string(setupDataStr.size()));
110 if (::rename(tmpChildSetupDataPath.c_str(), childSetupDataPath.c_str()) != 0) {
111 throw makeSystemErrorFromErrno(
112 "error renaming 'child setup data' file '"s + childSetupDataPath +
"'"s, errno);
116std::string tryReadChildSetupDataFile(
int childPid)
118 auto childSetupDataPath = getChildSetupDataPath(childPid);
120 auto openRes = ::open(childSetupDataPath.c_str(), O_RDONLY);
121 if (openRes < 0 && errno == ENOENT)
124 throw makeSystemErrorFromErrno(
125 "error opening 'child setup data' file '"s + childSetupDataPath +
"' for read"s, errno);
127 int fileFd = openRes;
129 char readBuffer[qChildSetupMaxSetupDataSize + 1];
130 auto readRes = ::read(fileFd, readBuffer,
sizeof(readBuffer));
131 int readErrno = errno;
133 (
void) ::close(fileFd);
136 throw makeSystemErrorFromErrno(
137 "error reading 'child setup data' file '"s + childSetupDataPath +
"'"s, readErrno);
139 auto readSize =
static_cast<
std::size_t>(readRes);
140 if (readSize > qChildSetupMaxSetupDataSize) {
141 throw std::runtime_error(
142 "received 'child setup data' file '"s + childSetupDataPath +
"' is too big"s);
143 }
else if (readSize == 0) {
144 throw std::runtime_error(
145 "received 'child setup data' file '"s + childSetupDataPath +
"' is empty"s);
148 return std::string(readBuffer, readSize);
151bool checkIfChildSetupDataFileExists(
int childPid)
153 return ::access(getChildSetupDataPath(childPid).c_str(), F_OK) == 0;
156void removeChildSetupDataFileIfExists(
int childPid)
158 (
void) ::unlink(getChildSetupDataPath(childPid).c_str());
165 namespace ch =
std::chrono;
167 auto childPid = ::getpid();
169 std::string setupDataStr;
171 auto startTime = ch::steady_clock::now();
172 auto timeoutEnd = startTime + ch::seconds(qChildSetupDataSendTimeoutSecs);
174 setupDataStr = tryReadChildSetupDataFile(childPid);
175 if (!setupDataStr.empty())
177 std::this_thread::sleep_for(qChildSetupDataSendRetryDelay);
178 }
while (ch::steady_clock::now() < timeoutEnd);
180 if (!setupDataStr.empty())
181 removeChildSetupDataFileIfExists(childPid);
183 throw std::runtime_error(
"timeout waiting for 'child setup data' file");
184 }
catch (
const std::exception &e) {
185 qOhosPrintfError(
"%s: error reading subprocess setup data: %s", Q_FUNC_INFO, e.what());
188 QNapi::Object global = env.Global();
190 return global.eval<QNapi::Object>(
"JSON.parse(*)", {setupDataStr});
191 }
catch (
const Napi::Error &e) {
192 qOhosPrintfError(
"%s: subprocess setup data has illegal format: %s", Q_FUNC_INFO, e.what());
193 return QNapi::makeObject(env);
199 namespace ch =
std::chrono;
201 auto senderThread = std::thread(
202 [childPid, setupData]() {
203 auto setupDataStr = QJsonDocument(setupData).toJson(QJsonDocument::Compact).toStdString();
206 writeChildSetupDataFile(childPid, setupDataStr);
207 }
catch (
const std::exception &e) {
208 qOhosPrintfError(
"%s: error writing subprocess setup data: %s", Q_FUNC_INFO, e.what());
212 auto startTime = ch::steady_clock::now();
213 auto timeoutEnd = startTime + ch::seconds(qChildSetupDataSendTimeoutSecs);
215 std::this_thread::sleep_for(qChildSetupDataSendRetryDelay);
216 }
while (checkIfChildSetupDataFileExists(childPid) && ch::steady_clock::now() < timeoutEnd);
218 if (checkIfChildSetupDataFileExists(childPid)) {
219 removeChildSetupDataFileIfExists(childPid);
220 qOhosPrintfError(
"%s: failed to send setup data to child %d", Q_FUNC_INFO, childPid);
223 senderThread.detach();
QNapi::Object readChildProcessSetupData(Napi::Env env)
void sendChildProcessSetupData(int childPid, QJsonObject setupData)