1#include "fifo.h"
2
3#include <base/log.h>
4#include <base/system.h>
5#if defined(CONF_FAMILY_UNIX)
6
7#include <fcntl.h>
8#include <sys/stat.h>
9#include <sys/types.h>
10#include <unistd.h>
11
12#include <cstdlib>
13
14void CFifo::Init(IConsole *pConsole, const char *pFifoFile, int Flag)
15{
16 m_File = -1;
17
18 m_pConsole = pConsole;
19 m_IsInit = true;
20 if(pFifoFile[0] == '\0')
21 return;
22
23 str_copy(dst&: m_aFilename, src: pFifoFile);
24 m_Flag = Flag;
25
26 bool IsFifo;
27 if(mkfifo(path: m_aFilename, mode: 0600) == 0)
28 {
29 struct stat Attribute;
30 if(stat(file: m_aFilename, buf: &Attribute) == 0)
31 {
32 IsFifo = S_ISFIFO(Attribute.st_mode);
33 }
34 else
35 {
36 log_warn("fifo", "Failed to stat fifo '%s' (%d '%s')", m_aFilename, errno, strerror(errno));
37 IsFifo = false;
38 }
39 }
40 else
41 {
42 log_warn("fifo", "Failed to create fifo '%s' (%d '%s')", m_aFilename, errno, strerror(errno));
43 IsFifo = false;
44 }
45 if(!IsFifo)
46 {
47 log_warn("fifo", "File '%s' is not a fifo, removing", m_aFilename);
48 if(fs_remove(filename: m_aFilename) != 0)
49 {
50 log_error("fifo", "Failed to remove non-fifo '%s'", m_aFilename); // details logged in fs_remove
51 return;
52 }
53 if(mkfifo(path: m_aFilename, mode: 0600) != 0)
54 {
55 log_error("fifo", "Failed to create fifo '%s' (%d '%s')", m_aFilename, errno, strerror(errno));
56 return;
57 }
58 struct stat Attribute;
59 if(stat(file: m_aFilename, buf: &Attribute) != 0)
60 {
61 log_error("fifo", "Failed to stat fifo '%s' (%d '%s')", m_aFilename, errno, strerror(errno));
62 return;
63 }
64 if(!S_ISFIFO(Attribute.st_mode))
65 {
66 log_error("fifo", "File '%s' is not a fifo", m_aFilename);
67 return;
68 }
69 }
70
71 m_File = open(file: m_aFilename, O_RDONLY | O_NONBLOCK);
72 if(m_File < 0)
73 {
74 log_error("fifo", "Failed to open fifo '%s' (%d '%s')", m_aFilename, errno, strerror(errno));
75 }
76 else
77 {
78 log_info("fifo", "Opened fifo '%s'", m_aFilename);
79 }
80}
81
82void CFifo::Shutdown()
83{
84 if(m_File < 0)
85 return;
86
87 close(fd: m_File);
88 fs_remove(filename: m_aFilename);
89}
90
91void CFifo::Update()
92{
93 if(m_File < 0)
94 return;
95
96 char aBuf[8192];
97 int Length = read(fd: m_File, buf: aBuf, nbytes: sizeof(aBuf) - 1);
98 if(Length <= 0)
99 return;
100 aBuf[Length] = '\0';
101
102 char *pCur = aBuf;
103 for(int i = 0; i < Length; ++i)
104 {
105 if(aBuf[i] != '\n')
106 continue;
107 aBuf[i] = '\0';
108 if(str_utf8_check(str: pCur))
109 {
110 m_pConsole->ExecuteLineFlag(pStr: pCur, FlasgMask: m_Flag, ClientId: IConsole::CLIENT_ID_UNSPECIFIED);
111 }
112 pCur = aBuf + i + 1;
113 }
114 if(pCur < aBuf + Length && str_utf8_check(str: pCur)) // missed the last line
115 {
116 m_pConsole->ExecuteLineFlag(pStr: pCur, FlasgMask: m_Flag, ClientId: IConsole::CLIENT_ID_UNSPECIFIED);
117 }
118}
119
120#elif defined(CONF_FAMILY_WINDOWS)
121
122#include <base/windows.h>
123
124#include <windows.h>
125
126void CFifo::Init(IConsole *pConsole, const char *pFifoFile, int Flag)
127{
128 m_pConsole = pConsole;
129 m_IsInit = true;
130 if(pFifoFile[0] == '\0')
131 {
132 m_pPipe = INVALID_HANDLE_VALUE;
133 return;
134 }
135
136 str_copy(m_aFilename, "\\\\.\\pipe\\");
137 str_append(m_aFilename, pFifoFile);
138 m_Flag = Flag;
139
140 const std::wstring WideFilename = windows_utf8_to_wide(m_aFilename);
141 m_pPipe = CreateNamedPipeW(WideFilename.c_str(),
142 PIPE_ACCESS_DUPLEX,
143 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS,
144 PIPE_UNLIMITED_INSTANCES,
145 8192,
146 8192,
147 NMPWAIT_USE_DEFAULT_WAIT,
148 nullptr);
149 if(m_pPipe == INVALID_HANDLE_VALUE)
150 {
151 const DWORD LastError = GetLastError();
152 log_error("fifo", "Failed to create named pipe '%s' (%ld '%s')", m_aFilename, LastError, windows_format_system_message(LastError).c_str());
153 }
154 else
155 {
156 log_info("fifo", "Created named pipe '%s'", m_aFilename);
157 }
158}
159
160void CFifo::Shutdown()
161{
162 if(m_pPipe == INVALID_HANDLE_VALUE)
163 return;
164
165 DisconnectNamedPipe(m_pPipe);
166 CloseHandle(m_pPipe);
167 m_pPipe = INVALID_HANDLE_VALUE;
168}
169
170void CFifo::Update()
171{
172 if(m_pPipe == INVALID_HANDLE_VALUE)
173 return;
174
175 if(!ConnectNamedPipe(m_pPipe, nullptr))
176 {
177 const DWORD LastError = GetLastError();
178 if(LastError == ERROR_PIPE_LISTENING) // waiting for clients to connect
179 return;
180 if(LastError == ERROR_NO_DATA) // pipe was disconnected from the other end, also disconnect it from this end
181 {
182 // disconnect the previous client so we can connect to a new one
183 DisconnectNamedPipe(m_pPipe);
184 return;
185 }
186 if(LastError != ERROR_PIPE_CONNECTED) // pipe already connected, not an error
187 {
188 log_error("fifo", "Failed to connect named pipe '%s' (%ld '%s')", m_aFilename, LastError, windows_format_system_message(LastError).c_str());
189 return;
190 }
191 }
192
193 while(true) // read all messages from the pipe
194 {
195 DWORD BytesAvailable;
196 if(!PeekNamedPipe(m_pPipe, nullptr, 0, nullptr, &BytesAvailable, nullptr))
197 {
198 const DWORD LastError = GetLastError();
199 if(LastError == ERROR_BROKEN_PIPE)
200 {
201 // Pipe was disconnected from the other side, either immediately
202 // after connecting or after reading the previous message.
203 DisconnectNamedPipe(m_pPipe);
204 }
205 else
206 {
207 log_error("fifo", "Failed to peek at pipe '%s' (%ld '%s')", m_aFilename, LastError, windows_format_system_message(LastError).c_str());
208 }
209 return;
210 }
211 if(BytesAvailable == 0) // pipe connected but no data available
212 return;
213
214 char *pBuf = static_cast<char *>(malloc(BytesAvailable + 1));
215 DWORD Length;
216 if(!ReadFile(m_pPipe, pBuf, BytesAvailable, &Length, nullptr))
217 {
218 const DWORD LastError = GetLastError();
219 log_error("fifo", "Failed to read from pipe '%s' (%ld '%s')", m_aFilename, LastError, windows_format_system_message(LastError).c_str());
220 free(pBuf);
221 return;
222 }
223 pBuf[Length] = '\0';
224
225 char *pCur = pBuf;
226 for(DWORD i = 0; i < Length; ++i)
227 {
228 if(pBuf[i] != '\n' && (pBuf[i] != '\r' || pBuf[i + 1] != '\n'))
229 {
230 continue;
231 }
232 if(pBuf[i] == '\r')
233 {
234 pBuf[i] = '\0';
235 ++i;
236 }
237 pBuf[i] = '\0';
238 if(pCur[0] != '\0' && str_utf8_check(pCur))
239 {
240 m_pConsole->ExecuteLineFlag(pCur, m_Flag, IConsole::CLIENT_ID_UNSPECIFIED);
241 }
242 pCur = pBuf + i + 1;
243 }
244 if(pCur < pBuf + Length && pCur[0] != '\0' && str_utf8_check(pCur)) // missed the last line
245 {
246 m_pConsole->ExecuteLineFlag(pCur, m_Flag, IConsole::CLIENT_ID_UNSPECIFIED);
247 }
248
249 free(pBuf);
250 }
251}
252#endif
253