ctkErrorLogFDMessageHandler.cpp 8.0 KB

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