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