5#include <QtCore/qbytearray.h>
6#include <QtCore/qjsondocument.h>
7#include <QtCore/private/qohoslogger_p.h>
16#include <sys/socket.h>
21#include <system_error>
25using namespace std::string_literals;
34const auto appTempDir =
"/data/storage/el2/base/temp";
36constexpr unsigned qChildSetupDataSendTimeoutSecs = 5;
38constexpr auto qChildSetupDataSendRetryDelay = std::chrono::milliseconds(50);
40constexpr std::size_t qChildSetupMaxSetupDataSize = 64 * 1024;
42std::system_error makeSystemErrorFromErrno(
const std::string &message,
int errnoValue)
44 return std::system_error(errnoValue,
std::generic_category(), message);
47std::string getSetupDataExchangeDirPath()
49 return appTempDir +
"/qohos-child-setup-data"s;
52std::string getChildSetupDataPath(
int childPid)
54 return getSetupDataExchangeDirPath() +
"/"s + std::to_string(childPid);
57void makeSetupDataExchangeDirIfNeeded()
59 auto setupDataExchangeDirPath = getSetupDataExchangeDirPath();
61 if (::mkdir(setupDataExchangeDirPath.c_str(), 0700) != 0 && errno != EEXIST) {
62 throw makeSystemErrorFromErrno(
63 "error creating 'child setup data' directory", errno);
66 struct ::stat statBuf;
67 if (::stat(setupDataExchangeDirPath.c_str(), &statBuf) != 0) {
68 throw makeSystemErrorFromErrno(
69 "stat() failed for 'child setup data' directory", errno);
72 if (!S_ISDIR(statBuf.st_mode))
73 throw std::runtime_error(
"non-directory found at 'child setup data' directory path");
76void writeChildSetupDataFile(
int childPid,
const std::string &setupDataStr)
78 auto childSetupDataPath = getChildSetupDataPath(childPid);
79 auto tmpChildSetupDataPath = childSetupDataPath +
".tmp"s;
81 makeSetupDataExchangeDirIfNeeded();
83 (
void) ::unlink(childSetupDataPath.c_str());
84 (
void) ::unlink(tmpChildSetupDataPath.c_str());
86 auto openRes = ::open(tmpChildSetupDataPath.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0600);
88 throw makeSystemErrorFromErrno(
89 "error opening 'child setup data' file '"s + tmpChildSetupDataPath +
"' for write"s, errno);
93 auto writeRes = ::write(fileFd, setupDataStr.data(), setupDataStr.size());
94 int writeErrno = errno;
96 (
void) ::close(fileFd);
99 (
void) ::unlink(tmpChildSetupDataPath.c_str());
100 throw makeSystemErrorFromErrno(
101 "error writing to 'child setup data' file '"s + tmpChildSetupDataPath +
"'"s, writeErrno);
103 auto writtenSize =
static_cast<
std::size_t>(writeRes);
104 if (writtenSize != setupDataStr.size()) {
105 (
void) ::unlink(tmpChildSetupDataPath.c_str());
106 throw std::runtime_error(
107 "incomplete write to 'child setup data' file: "s
108 + std::to_string(writtenSize) +
"/"s + std::to_string(setupDataStr.size()));
111 if (::rename(tmpChildSetupDataPath.c_str(), childSetupDataPath.c_str()) != 0) {
112 throw makeSystemErrorFromErrno(
113 "error renaming 'child setup data' file '"s + childSetupDataPath +
"'"s, errno);
117std::string tryReadChildSetupDataFile(
int childPid)
119 auto childSetupDataPath = getChildSetupDataPath(childPid);
121 auto openRes = ::open(childSetupDataPath.c_str(), O_RDONLY);
122 if (openRes < 0 && errno == ENOENT)
125 throw makeSystemErrorFromErrno(
126 "error opening 'child setup data' file '"s + childSetupDataPath +
"' for read"s, errno);
128 int fileFd = openRes;
130 char readBuffer[qChildSetupMaxSetupDataSize + 1];
131 auto readRes = ::read(fileFd, readBuffer,
sizeof(readBuffer));
132 int readErrno = errno;
134 (
void) ::close(fileFd);
137 throw makeSystemErrorFromErrno(
138 "error reading 'child setup data' file '"s + childSetupDataPath +
"'"s, readErrno);
140 auto readSize =
static_cast<
std::size_t>(readRes);
141 if (readSize > qChildSetupMaxSetupDataSize) {
142 throw std::runtime_error(
143 "received 'child setup data' file '"s + childSetupDataPath +
"' is too big"s);
144 }
else if (readSize == 0) {
145 throw std::runtime_error(
146 "received 'child setup data' file '"s + childSetupDataPath +
"' is empty"s);
149 return std::string(readBuffer, readSize);
152bool checkIfChildSetupDataFileExists(
int childPid)
154 return ::access(getChildSetupDataPath(childPid).c_str(), F_OK) == 0;
157void removeChildSetupDataFileIfExists(
int childPid)
159 (
void) ::unlink(getChildSetupDataPath(childPid).c_str());
166 namespace ch =
std::chrono;
168 auto childPid = ::getpid();
170 std::string setupDataStr;
172 auto startTime = ch::steady_clock::now();
173 auto timeoutEnd = startTime + ch::seconds(qChildSetupDataSendTimeoutSecs);
175 setupDataStr = tryReadChildSetupDataFile(childPid);
176 if (!setupDataStr.empty())
178 std::this_thread::sleep_for(qChildSetupDataSendRetryDelay);
179 }
while (ch::steady_clock::now() < timeoutEnd);
181 if (!setupDataStr.empty())
182 removeChildSetupDataFileIfExists(childPid);
184 throw std::runtime_error(
"timeout waiting for 'child setup data' file");
185 }
catch (
const std::exception &e) {
186 qOhosPrintfError(
"%s: error reading subprocess setup data: %s", Q_FUNC_INFO, e.what());
189 auto setupDataDoc = QJsonDocument::fromJson(QByteArray::fromStdString(setupDataStr));
190 if (!setupDataDoc.isObject())
191 qOhosPrintfError(
"%s: subprocess setup data has illegal format", Q_FUNC_INFO);
193 return setupDataDoc.object();
198 namespace ch =
std::chrono;
200 auto senderThread = std::thread(
201 [childPid, setupData]() {
202 auto setupDataStr = QJsonDocument(setupData).toJson(QJsonDocument::Compact).toStdString();
205 writeChildSetupDataFile(childPid, setupDataStr);
206 }
catch (
const std::exception &e) {
207 qOhosPrintfError(
"%s: error writing subprocess setup data: %s", Q_FUNC_INFO, e.what());
211 auto startTime = ch::steady_clock::now();
212 auto timeoutEnd = startTime + ch::seconds(qChildSetupDataSendTimeoutSecs);
214 std::this_thread::sleep_for(qChildSetupDataSendRetryDelay);
215 }
while (checkIfChildSetupDataFileExists(childPid) && ch::steady_clock::now() < timeoutEnd);
217 if (checkIfChildSetupDataFileExists(childPid)) {
218 removeChildSetupDataFileIfExists(childPid);
219 qOhosPrintfError(
"%s: failed to send setup data to child %d", Q_FUNC_INFO, childPid);
222 senderThread.detach();
QJsonObject readChildProcessSetupData()
void sendChildProcessSetupData(int childPid, QJsonObject setupData)