Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : /*
4 : : MIT License
5 : :
6 : : Copyright (c) 2014-2024 Stephane Cuillerdier (aka aiekick)
7 : :
8 : : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : of this software and associated documentation files (the "Software"), to deal
10 : : in the Software without restriction, including without limitation the rights
11 : : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : copies of the Software, and to permit persons to whom the Software is
13 : : furnished to do so, subject to the following conditions:
14 : :
15 : : The above copyright notice and this permission notice shall be included in all
16 : : copies or substantial portions of the Software.
17 : :
18 : : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : : SOFTWARE.
25 : : */
26 : :
27 : : // ezNamedPipes is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
28 : :
29 : : #include "ezOS.hpp"
30 : :
31 : : #include <vector>
32 : : #include <string>
33 : : #include <cstdint>
34 : : #include <memory>
35 : : #include <stdexcept>
36 : : #include <iostream>
37 : :
38 : : #ifdef WINDOWS_OS
39 : : #include <Windows.h>
40 : : #else
41 : : #include <sys/types.h>
42 : : #include <sys/stat.h>
43 : : #include <dirent.h>
44 : : #include <unistd.h>
45 : : #include <fcntl.h>
46 : : #include <cstring>
47 : : #endif
48 : :
49 : : namespace ez {
50 : :
51 : : class NamedPipe {
52 : : public:
53 : : typedef std::vector<char> DatasBuffer;
54 : :
55 : : public:
56 : 0 : static std::vector<std::string> getActivePipes() {
57 : 0 : std::vector<std::string> pipeNames;
58 : 0 : #ifdef WINDOWS_OS
59 : 0 : char buffer[4096];
60 : 0 :
61 : 0 : // Buffer pour les résultats
62 : 0 : WIN32_FIND_DATAA findFileData;
63 : 0 : HANDLE hFind = FindFirstFileA("\\\\.\\pipe\\*", &findFileData);
64 : 0 :
65 : 0 : if (hFind == INVALID_HANDLE_VALUE) {
66 : 0 : std::cerr << "Failed to list named pipes. Error: " << GetLastError() << std::endl;
67 : 0 : return pipeNames;
68 : 0 : }
69 : 0 :
70 : 0 : do {
71 : 0 : pipeNames.push_back(findFileData.cFileName); // Ajoute le nom du pipe à la liste
72 : 0 : } while (FindNextFileA(hFind, &findFileData)); // Continue à chercher
73 : 0 :
74 : 0 : FindClose(hFind); // Ferme le handle de recherche
75 : 0 : #else
76 : 0 : const auto directory = std::string("/tmp");
77 : 0 : DIR* dir = opendir(directory.c_str());
78 : 0 : if (dir) {
79 : 0 : struct dirent* entry;
80 : 0 : struct stat fileStat{};
81 : 0 : while ((entry = readdir(dir)) != nullptr) {
82 : 0 : std::string fullPath = directory + "/" + entry->d_name;
83 : 0 : if (stat(fullPath.c_str(), &fileStat) == -1) {
84 : 0 : std::cerr << "Failed to stat file: " << fullPath << std::endl;
85 : 0 : continue;
86 : 0 : }
87 : 0 : if (S_ISFIFO(fileStat.st_mode)) {
88 : 0 : pipeNames.push_back(fullPath);
89 : 0 : }
90 : 0 : }
91 : 0 : closedir(dir);
92 : 0 : }
93 : 0 : #endif
94 : 0 : return pipeNames;
95 : 0 : }
96 : :
97 : : private:
98 : : class Backend {
99 : : private:
100 : : size_t m_lastMessageSize{0};
101 : : std::vector<char> m_buffer;
102 : : std::string m_pipeName;
103 : : bool m_isServer{};
104 : : #ifdef WINDOWS_OS
105 : : HANDLE m_pipeHandle = INVALID_HANDLE_VALUE;
106 : : #else
107 : : int32_t m_pipeFd = -1;
108 : : #endif
109 : :
110 : : public:
111 : 2 : virtual ~Backend() { unit(); }
112 : :
113 : 6 : void unit() {
114 : : #ifdef WINDOWS_OS
115 : : if (m_pipeHandle != INVALID_HANDLE_VALUE) {
116 : : CloseHandle(m_pipeHandle);
117 : : }
118 : : m_pipeHandle = INVALID_HANDLE_VALUE;
119 : : #else
120 [ + + ]: 6 : if (m_pipeFd != -1) {
121 : 2 : close(m_pipeFd);
122 [ + + ]: 2 : if (m_isServer) {
123 : 1 : unlink(m_pipeName.c_str());
124 : 1 : }
125 : 2 : }
126 : 6 : m_pipeFd = -1;
127 : 6 : #endif
128 : 6 : }
129 : :
130 : 3 : bool writeBuffer(const DatasBuffer& vMessage) {
131 : : #ifdef WINDOWS_OS
132 : : DWORD bytesWritten;
133 : : if (!WriteFile(m_pipeHandle, vMessage.data(), vMessage.size(), &bytesWritten, nullptr)) {
134 : : return false;
135 : : }
136 : : #else
137 : 3 : auto bytesWritten = ::write(m_pipeFd, vMessage.data(), vMessage.size());
138 [ + + ]: 3 : if (bytesWritten == -1) {
139 : 1 : return false;
140 : 1 : }
141 : 2 : #endif
142 : 2 : return true;
143 : 3 : }
144 : :
145 : 3 : bool writeString(const std::string& vMessage) {
146 [ + - ]: 3 : if (!vMessage.empty()) {
147 : 3 : const ez::NamedPipe::DatasBuffer buffer(vMessage.begin(), vMessage.end());
148 : 3 : return writeBuffer(buffer);
149 : 3 : }
150 : 0 : return false;
151 : 3 : }
152 : :
153 : 2 : bool isMessageReceived() {
154 : : #ifdef WINDOWS_OS
155 : : DWORD bytesRead;
156 : : if (ReadFile(m_pipeHandle, m_buffer.data(), m_buffer.size(), &bytesRead, nullptr) == TRUE) {
157 : : m_lastMessageSize = bytesRead;
158 : : return true;
159 : : }
160 : : return false;
161 : : #else
162 : 2 : auto bytesRead = ::read(m_pipeFd, m_buffer.data(), m_buffer.size());
163 [ + + ]: 2 : if (bytesRead != -1) {
164 : 1 : m_lastMessageSize = bytesRead;
165 : 1 : return true;
166 : 1 : }
167 : 1 : return false;
168 : 2 : #endif
169 : 2 : }
170 : :
171 : 0 : DatasBuffer readBuffer(size_t& vOutSize) {
172 : 0 : vOutSize = m_lastMessageSize;
173 : 0 : return m_buffer;
174 : 0 : }
175 : :
176 : 2 : std::string readString() {
177 [ + - ]: 2 : if (m_lastMessageSize) {
178 : 2 : return std::string(m_buffer.data(), m_lastMessageSize);
179 : 2 : }
180 : 0 : return {};
181 : 2 : }
182 : :
183 : : protected:
184 : 1 : bool m_initServer(const std::string& vPipeName, size_t vBufferSize, int32_t vMaxInstances = 1) {
185 : 1 : m_isServer = true;
186 : 1 : m_buffer.resize(vBufferSize);
187 : : #ifdef WINDOWS_OS
188 : : m_pipeName = "\\\\.\\pipe\\" + vPipeName;
189 : : m_pipeHandle = CreateNamedPipe( //
190 : : m_pipeName.c_str(),
191 : : PIPE_ACCESS_DUPLEX,
192 : : PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
193 : : vMaxInstances, // Max instances
194 : : vBufferSize, // Output buffer size
195 : : vBufferSize, // Input buffer size
196 : : 0, // Default timeout
197 : : nullptr // Default security attributes
198 : : );
199 : : if (m_pipeHandle == INVALID_HANDLE_VALUE) {
200 : : return false;
201 : : }
202 : : #else
203 : 1 : m_pipeName = "/tmp/" + vPipeName;
204 [ - + ][ # # ]: 1 : if (mkfifo(m_pipeName.c_str(), 0666) == -1 && errno != EEXIST) {
205 : 0 : return false;
206 : 0 : }
207 : 1 : m_pipeFd = open(m_pipeName.c_str(), O_RDWR);
208 [ - + ]: 1 : if (m_pipeFd == -1) {
209 : 0 : return false;
210 : 0 : }
211 : 1 : #endif
212 : 1 : return true;
213 : 1 : }
214 : :
215 : : bool m_initClient(const std::string& vPipeName
216 : : #ifdef EZ_TIME
217 : : , size_t vTimeOutInMs = 1000u
218 : : #endif
219 : 1 : ) {
220 : 1 : m_isServer = false;
221 : : #ifdef WINDOWS_OS
222 : : m_pipeName = "\\\\.\\pipe\\" + vPipeName;
223 : : #ifdef EZ_TIME
224 : : const auto start = ez::time::getTicks();
225 : : #endif
226 : : #ifdef EZ_TIME
227 : : while (m_pipeHandle == INVALID_HANDLE_VALUE) {
228 : : #endif
229 : : m_pipeHandle = CreateFile( //
230 : : m_pipeName.c_str(),
231 : : GENERIC_READ | GENERIC_WRITE,
232 : : 0,
233 : : nullptr,
234 : : OPEN_ALWAYS,
235 : : 0,
236 : : nullptr);
237 : : #ifdef EZ_TIME
238 : : if (m_pipeHandle == INVALID_HANDLE_VALUE && //
239 : : (ez::time::getTicks() - start) > vTimeOutInMs) {
240 : : break;
241 : : }
242 : : }
243 : : #endif
244 : : if (m_pipeHandle == INVALID_HANDLE_VALUE) {
245 : : return false;
246 : : }
247 : : #else
248 : 1 : m_pipeName = "/tmp/" + vPipeName;
249 : 1 : m_pipeFd = open(m_pipeName.c_str(), O_RDWR);
250 [ - + ]: 1 : if (m_pipeFd == -1) {
251 : 0 : return false;
252 : 0 : }
253 : 1 : #endif
254 : 1 : return true;
255 : 1 : }
256 : : };
257 : :
258 : : public:
259 : : class Server : public Backend {
260 : : public:
261 : : typedef std::shared_ptr<Server> Ptr;
262 : : typedef std::weak_ptr<Server> Weak;
263 : :
264 : : public:
265 : 1 : static Ptr create(const std::string& pipeName, size_t vBufferSize, int32_t vMaxInstances = 1) {
266 : 1 : auto ret = std::make_shared<Server>();
267 [ - + ]: 1 : if (!ret->m_initServer(pipeName, vBufferSize, vMaxInstances)) {
268 : 0 : ret.reset();
269 : 0 : }
270 : 1 : return ret;
271 : 1 : }
272 : :
273 : 1 : ~Server() override { unit(); }
274 : : };
275 : :
276 : :
277 : : class Client : public Backend {
278 : : public:
279 : : typedef std::shared_ptr<Client> Ptr;
280 : : typedef std::weak_ptr<Client> Weak;
281 : :
282 : : public:
283 : 1 : static Ptr create(const std::string& pipeName) {
284 : 1 : auto ret = std::make_shared<Client>();
285 [ - + ]: 1 : if (!ret->m_initClient(pipeName)) {
286 : 0 : ret.reset();
287 : 0 : }
288 : 1 : return ret;
289 : 1 : }
290 : :
291 : 1 : ~Client() override { unit(); }
292 : : };
293 : : };
294 : :
295 : : } // namespace ez
|