ctkErrorLogFDMessageHandler.cpp 8.2 KB

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