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