ctkErrorLogFDMessageHandler.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QDebug>
  16. #include <QFile>
  17. // CTK includes
  18. #include "ctkErrorLogContext.h"
  19. #include "ctkErrorLogFDMessageHandler.h"
  20. #include "ctkErrorLogFDMessageHandler_p.h"
  21. #include "ctkUtils.h"
  22. // STD includes
  23. #include <cstdio>
  24. #ifdef Q_OS_WIN32
  25. # include <fcntl.h> // For _O_TEXT
  26. # include <io.h> // For _pipe, _dup and _dup2
  27. #else
  28. # include <unistd.h> // For pipe, dup and dup2
  29. #endif
  30. // --------------------------------------------------------------------------
  31. // ctkFDHandler methods
  32. // See http://stackoverflow.com/questions/5419356/redirect-stdout-stderr-to-a-string
  33. // and http://stackoverflow.com/questions/955962/how-to-buffer-stdout-in-memory-and-write-it-from-a-dedicated-thread
  34. // --------------------------------------------------------------------------
  35. ctkFDHandler::ctkFDHandler(ctkErrorLogFDMessageHandler* messageHandler,
  36. ctkErrorLogLevel::LogLevel logLevel,
  37. ctkErrorLogTerminalOutput::TerminalOutput terminalOutput)
  38. {
  39. this->MessageHandler = messageHandler;
  40. this->LogLevel = logLevel;
  41. this->TerminalOutput = terminalOutput;
  42. this->SavedFDNumber = 0;
  43. this->Pipe[0] = -1;
  44. this->Pipe[1] = -1;
  45. this->Enabled = false;
  46. }
  47. // --------------------------------------------------------------------------
  48. ctkFDHandler::~ctkFDHandler()
  49. {
  50. }
  51. // --------------------------------------------------------------------------
  52. void ctkFDHandler::setupPipe()
  53. {
  54. #ifdef Q_OS_WIN32
  55. int status = _pipe(this->Pipe, 65536, _O_TEXT);
  56. #else
  57. int status = pipe(this->Pipe);
  58. #endif
  59. if (status != 0)
  60. {
  61. qCritical().nospace() << "ctkFDHandler - Failed to create pipe !";
  62. return;
  63. }
  64. }
  65. // --------------------------------------------------------------------------
  66. FILE* ctkFDHandler::terminalOutputFile()
  67. {
  68. return this->TerminalOutput == ctkErrorLogTerminalOutput::StandardOutput ? stdout : stderr;
  69. }
  70. // --------------------------------------------------------------------------
  71. void ctkFDHandler::setEnabled(bool value)
  72. {
  73. if (this->Enabled == value)
  74. {
  75. return;
  76. }
  77. if (value)
  78. {
  79. this->setupPipe();
  80. // Flush (stdout|stderr) so that any buffered messages are delivered
  81. fflush(this->terminalOutputFile());
  82. // Save position of current standard output
  83. fgetpos(this->terminalOutputFile(), &this->SavedFDPos);
  84. #ifdef Q_OS_WIN32
  85. this->SavedFDNumber = _dup(_fileno(this->terminalOutputFile()));
  86. _dup2(this->Pipe[1], _fileno(this->terminalOutputFile()));
  87. _close(this->Pipe[1]);
  88. #else
  89. this->SavedFDNumber = dup(fileno(this->terminalOutputFile()));
  90. dup2(this->Pipe[1], fileno(this->terminalOutputFile()));
  91. close(this->Pipe[1]);
  92. #endif
  93. // Start polling thread
  94. this->Enabled = true;
  95. this->start();
  96. }
  97. else
  98. {
  99. // Print one character to "unblock" the read function associated with the polling thread
  100. #ifdef Q_OS_WIN32
  101. int unused = _write(_fileno(this->terminalOutputFile()), "\n", 1);
  102. #else
  103. ssize_t unused = write(fileno(this->terminalOutputFile()), "\n", 1);
  104. #endif
  105. Q_UNUSED(unused);
  106. // Flush stdout or stderr so that any buffered messages are delivered
  107. fflush(this->terminalOutputFile());
  108. // Stop polling thread
  109. {
  110. QMutexLocker locker(&this->EnableMutex);
  111. this->Enabled = false;
  112. }
  113. QString newline("\n");
  114. #ifdef Q_OS_WIN32
  115. unused = _write(_fileno(this->terminalOutputFile()), qPrintable(newline), newline.size());
  116. #else
  117. unused = write(fileno(this->terminalOutputFile()), qPrintable(newline), newline.size());
  118. #endif
  119. Q_UNUSED(unused);
  120. // Wait the polling thread graciously terminates
  121. this->wait();
  122. // Close files and restore standard output to stdout or stderr - which should be the terminal
  123. #ifdef Q_OS_WIN32
  124. _dup2(this->SavedFDNumber, _fileno(this->terminalOutputFile()));
  125. _close(this->SavedFDNumber);
  126. #else
  127. dup2(this->SavedFDNumber, fileno(this->terminalOutputFile()));
  128. close(this->SavedFDNumber);
  129. #endif
  130. clearerr(this->terminalOutputFile());
  131. fsetpos(this->terminalOutputFile(), &this->SavedFDPos);
  132. #ifdef Q_OS_WIN32
  133. _close(this->Pipe[0]);
  134. #else
  135. close(this->Pipe[0]);
  136. #endif
  137. this->SavedFDNumber = 0;
  138. }
  139. ctkErrorLogTerminalOutput * terminalOutput =
  140. this->MessageHandler->terminalOutput(this->TerminalOutput);
  141. if(terminalOutput)
  142. {
  143. terminalOutput->setFileDescriptor(this->SavedFDNumber);
  144. }
  145. }
  146. // --------------------------------------------------------------------------
  147. bool ctkFDHandler::enabled()const
  148. {
  149. QMutexLocker locker(&this->EnableMutex);
  150. return this->Enabled;
  151. }
  152. // --------------------------------------------------------------------------
  153. void ctkFDHandler::run()
  154. {
  155. while(true)
  156. {
  157. char c = '\0';
  158. QString line;
  159. while(c != '\n')
  160. {
  161. #ifdef Q_OS_WIN32
  162. int res = _read(this->Pipe[0], &c, 1); // When used with pipe, read() is blocking
  163. #else
  164. ssize_t res = read(this->Pipe[0], &c, 1); // When used with pipe, read() is blocking
  165. #endif
  166. if (res == -1)
  167. {
  168. break;
  169. }
  170. if (c != '\n')
  171. {
  172. line += c;
  173. }
  174. }
  175. if (!this->enabled())
  176. {
  177. break;
  178. }
  179. Q_ASSERT(this->MessageHandler);
  180. this->MessageHandler->handleMessage(
  181. ctk::qtHandleToString(QThread::currentThreadId()),
  182. this->LogLevel,
  183. this->MessageHandler->handlerPrettyName(),
  184. ctkErrorLogContext(line),
  185. line);
  186. }
  187. }
  188. // --------------------------------------------------------------------------
  189. // ctkErrorLogFDMessageHandlerPrivate
  190. // --------------------------------------------------------------------------
  191. class ctkErrorLogFDMessageHandlerPrivate
  192. {
  193. public:
  194. ctkErrorLogFDMessageHandlerPrivate();
  195. ~ctkErrorLogFDMessageHandlerPrivate();
  196. ctkFDHandler * StdOutFDHandler;
  197. ctkFDHandler * StdErrFDHandler;
  198. };
  199. // --------------------------------------------------------------------------
  200. // ctkErrorLogFDMessageHandlerPrivate methods
  201. //------------------------------------------------------------------------------
  202. ctkErrorLogFDMessageHandlerPrivate::ctkErrorLogFDMessageHandlerPrivate()
  203. {
  204. }
  205. //------------------------------------------------------------------------------
  206. ctkErrorLogFDMessageHandlerPrivate::~ctkErrorLogFDMessageHandlerPrivate()
  207. {
  208. delete this->StdOutFDHandler;
  209. delete this->StdErrFDHandler;
  210. }
  211. //------------------------------------------------------------------------------
  212. // ctkErrorLogFDMessageHandler methods
  213. //------------------------------------------------------------------------------
  214. QString ctkErrorLogFDMessageHandler::HandlerName = QLatin1String("FD");
  215. // --------------------------------------------------------------------------
  216. ctkErrorLogFDMessageHandler::ctkErrorLogFDMessageHandler() :
  217. Superclass(), d_ptr(new ctkErrorLogFDMessageHandlerPrivate())
  218. {
  219. Q_D(ctkErrorLogFDMessageHandler);
  220. d->StdOutFDHandler = new ctkFDHandler(this, ctkErrorLogLevel::Info, ctkErrorLogTerminalOutput::StandardOutput);
  221. d->StdErrFDHandler = new ctkFDHandler(this, ctkErrorLogLevel::Critical, ctkErrorLogTerminalOutput::StandardError);
  222. }
  223. // --------------------------------------------------------------------------
  224. ctkErrorLogFDMessageHandler::~ctkErrorLogFDMessageHandler()
  225. {
  226. }
  227. // --------------------------------------------------------------------------
  228. QString ctkErrorLogFDMessageHandler::handlerName()const
  229. {
  230. return ctkErrorLogFDMessageHandler::HandlerName;
  231. }
  232. // --------------------------------------------------------------------------
  233. void ctkErrorLogFDMessageHandler::setEnabledInternal(bool value)
  234. {
  235. Q_D(ctkErrorLogFDMessageHandler);
  236. d->StdOutFDHandler->setEnabled(value);
  237. d->StdErrFDHandler->setEnabled(value);
  238. }