/bin/php/ezasynchronouspublisher.php

https://github.com/GunioRobot/ezpublish · PHP · 195 lines · 131 code · 30 blank · 34 comment · 10 complexity · fca653e9ecdbcde877c6df5656e2008a MD5 · raw file

  1. #!/usr/bin/env php
  2. <?php
  3. /**
  4. *
  5. * @copyright Copyright (C) 1999-2011 eZ Systems AS. All rights reserved.
  6. * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
  7. * @version //autogentag//
  8. * @package
  9. */
  10. declare( ticks=1 );
  11. require 'autoload.php';
  12. $cli = eZCLI::instance();
  13. $script = eZScript::instance( array( 'description' => "Processes the eZ Publish publishing queue",
  14. 'use-session' => false,
  15. 'use-modules' => true,
  16. 'use-extensions' => true ) );
  17. $script->startup();
  18. $options = $script->getOptions(
  19. // options definition
  20. "[n|daemon][p:|pid-file:]",
  21. // arguments definition
  22. "",
  23. // options documentation
  24. array( 'daemon' => 'Run in the background',
  25. 'pid-file' => 'PID file' ) );
  26. $sys = eZSys::instance();
  27. $script->initialize();
  28. if ( isset( $options['pid-file'] ) )
  29. {
  30. $pidFile = $options['pid-file'];
  31. }
  32. else
  33. {
  34. $siteINI = eZINI::instance( 'site.ini' );
  35. $varDir = $siteINI->variable( 'FileSettings', 'VarDir' );
  36. $pidFile = "$varDir/run/ezasynchronouspublisher.pid";
  37. }
  38. // check if run folder exists
  39. $pidFileDirectory = dirname( $pidFile );
  40. if ( !file_exists( $pidFileDirectory ) )
  41. {
  42. if ( !mkdir( $pidFileDirectory ) )
  43. {
  44. $script->shutdown( 3, "Error creating PID file directory '$pidFileDirectory'" );
  45. }
  46. }
  47. // try opening the PID file. Exclusive mode will prevent the file from being opened if it exists
  48. $pidFp = @fopen( $pidFile, 'x' );
  49. if ( $pidFp === false )
  50. {
  51. // failed on exclusive creation, see if the file is locked
  52. $pidFp = fopen( $pidFile, 'r' );
  53. // lock obtained: the owner process has died without removing the file
  54. if ( flock( $pidFp, LOCK_EX | LOCK_NB ) === false )
  55. {
  56. fclose( $pidFp );
  57. $script->shutdown( 2, "Another instance of the daemon is already running with the same pid file" );
  58. }
  59. else
  60. {
  61. $cli->output( "Unclean shutdown has occured, taking ownership of the PID file" );
  62. }
  63. }
  64. else
  65. {
  66. flock( $pidFp, LOCK_EX | LOCK_NB );
  67. }
  68. // PID file IS locked after that point
  69. if ( $options['daemon'] )
  70. {
  71. // Trap signals that we expect to recieve
  72. pcntl_signal( SIGCHLD, 'childHandler' );
  73. pcntl_signal( SIGUSR1, 'childHandler' );
  74. pcntl_signal( SIGALRM, 'childHandler' );
  75. // close the PID file, and reopen it after forking
  76. fclose( $pidFp );
  77. $pid = pcntl_fork();
  78. if ( $pid < 0 )
  79. {
  80. error_log( "unable to fork daemon" );
  81. $script->shutdown( 1, "unable to fork daemon" );
  82. }
  83. // If we got a good PID, then we can wait until the daemon tells us to terminate
  84. if ( $pid > 0 )
  85. {
  86. // Wait for confirmation from the child via SIGTERM or SIGCHLD, or
  87. // for two seconds to elapse (SIGALRM). pause() should not return. */
  88. sleep( 10 );
  89. $script->shutdown( 1, "Failed spawning the daemon process" );
  90. }
  91. $pidFp = fopen( $pidFile, 'w' );
  92. flock( $pidFp, LOCK_EX | LOCK_NB );
  93. // At this point we are executing as the child process
  94. $parentProcessID = posix_getppid();
  95. /* Cancel certain signals */
  96. pcntl_signal( SIGCHLD, SIG_DFL ); // A child process dies
  97. pcntl_signal( SIGTSTP, SIG_IGN ); // Various TTY signals
  98. pcntl_signal( SIGTTOU, SIG_IGN );
  99. pcntl_signal( SIGTTIN, SIG_IGN );
  100. pcntl_signal( SIGHUP, SIG_IGN ); // Ignore hangup signal
  101. $sid = posix_setsid();
  102. if ( $sid < 0 )
  103. {
  104. error_log( "unable to create a new session" );
  105. $script->shutdown( 1, 'unable to create a new session' );
  106. }
  107. pcntl_signal( SIGTERM, 'daemonSignalHandler' );
  108. $pid = getmypid();
  109. $cli->output( "Publishing daemon started. Process ID: $pid" );
  110. fputs( $pidFp, $pid );
  111. // stop output completely
  112. fclose( STDIN );
  113. fclose( STDOUT );
  114. fclose( STDERR );
  115. // terminate the parent
  116. posix_kill( $parentProcessID, SIGUSR1 );
  117. }
  118. else
  119. {
  120. $cli->output( "Running in interactive mode. Hit ctrl-c to interrupt." );
  121. }
  122. if ( $options['daemon'] )
  123. {
  124. $output = new ezpAsynchronousPublisherLogOutput();
  125. }
  126. else
  127. {
  128. $output = new ezpAsynchronousPublisherCliOutput();
  129. }
  130. // actual daemon code
  131. $processor = ezpContentPublishingQueueProcessor::instance();
  132. $processor->setOutput( $output );
  133. $processor->run();
  134. eZScript::instance()->shutdown( 0 );
  135. /**
  136. * Signal handler
  137. * @param int $signo Signal number
  138. */
  139. function childHandler( $signo )
  140. {
  141. switch( $signo )
  142. {
  143. case SIGALRM: eZScript::instance()->shutdown( 1 ); break;
  144. case SIGUSR1: eZScript::instance()->shutdown( 0 ); break;
  145. case SIGCHLD: eZScript::instance()->shutdown( 1 ); break;
  146. }
  147. }
  148. /**
  149. * Signal handler for the daemon process
  150. * @param int $signo Signal number
  151. */
  152. function daemonSignalHandler( $signo )
  153. {
  154. switch( $signo )
  155. {
  156. case SIGTERM:
  157. flock( $GLOBALS['pidFp'], LOCK_UN );
  158. fclose( $GLOBALS['pidFp'] );
  159. ezpContentPublishingQueueProcessor::terminate();
  160. @unlink( $GLOBALS['pidFile'] );
  161. eZScript::instance()->shutdown( 0 );
  162. break;
  163. }
  164. }
  165. ?>