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