sloodled.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <?php
  2. /*
  3. sloodled.php, Edmund Edgar, 2012-04
  4. Copyright contributors, licensed under the same license as the rest of SLOODLE.
  5. This script is a background daemon used to improve performance when sending messages via http-in.
  6. It relies on having the Beanstalk daemon installed and running, and turned on in sloodle_config.php
  7. In a normal SLOODLE install you probably won't need to use it.
  8. You would normally run it in the background with something like:
  9. nohup php sloodled.php -m > /dev/null 2>&1 &
  10. Run with the -m flag, it will monitor the beanstalkd queue for tubes
  11. ...each corresponding to a particular combination of a task and an http-in address
  12. It spawns a copy of itself as a worker process to handle each tube, which will run for 90 seconds or so then exit.
  13. If you want to see it in action, run it with the -v flag (verbose)
  14. php sloodled.php -m -v
  15. If you've set things like SLOODLE_MESSAGE_QUEUE_SITE_PATH_PREFIX,
  16. ...it will check which site the task belongs to and change to the directory of that site to run the worker process.
  17. This is designed for Avatar Classroom, and contains some assumptions about the directory layout of multiple sites.
  18. As such, it probably won't work for anybody else without modification.
  19. */
  20. define('CLI_SCRIPT',true);
  21. if (file_exists('sloodle_config.php')) {
  22. require_once('sloodle_config.php');
  23. }
  24. define('SLOODLED_BASE_DIR', dirname(__FILE__));
  25. // This is useful in the Avatar Classroom multiple-host environment
  26. // ...where we want to be able to tell the script which database etc to write to
  27. if (!isset($_SERVER['SERVER_NAME'])) {
  28. $_SERVER = array();
  29. $_SERVER['SERVER_NAME'] = SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PREFIX.SLOODLE_MESSAGE_QUEUE_URL_SUFFIX;
  30. //SLOODLE_MESSAGE_QUEUE_SITE_PATH_PREFIX.SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PREFIX.SLOODLE_MESSAGE_QUEUE_SITE_PATH_SUFFIX;
  31. }
  32. if ( !defined('SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK') || !SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK ) {
  33. echo 'To run the sloodle daemon, enable SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK in sloodle_config.php';
  34. exit;
  35. }
  36. define('SLOODLE_MESSAGE_QUEUE_TASK', true);
  37. //require_once('lib/beanstalk/Beanstalk.php');
  38. require_once(SLOODLED_BASE_DIR.'/lib/beanstalk/Beanstalk.php');
  39. /*
  40. $sbhost = ( ( defined(SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_HOST) && (SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_HOST != '') ) ) ? SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_HOST : '127.0.0.1';
  41. $sbport = ( defined(SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PORT) && SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PORT ) ? SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PORT : 11300;
  42. $sbtimeout = ( defined(SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_TIMEOUT) && SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_TIMEOUT ) ? SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_TIMEOUT : 1;
  43. $sbpersistent = ( defined(SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PERSISTENT) && SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PERSISTENT ) ? SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PERSISTENT : true;
  44. $sbconfig = array(
  45. 'persistent' => $sbpersistent,
  46. 'host' => $sbhost,
  47. 'port' => $sbport,
  48. 'timeout' => $sbtimeout
  49. );
  50. */
  51. $args = $argv;
  52. array_shift($args); // remove the script name
  53. // Check if the array contains the "-v", and if it does remove it from the array.
  54. $verbose = in_array( '-v', $args );
  55. $args = array_diff($args, array('-v') );
  56. $stats = in_array( '-s', $args );
  57. $args = array_diff($args, array('-s') );
  58. $manage = in_array( '-m', $args );
  59. $args = array_diff($args, array('-m') );
  60. $list_tubes = in_array( '-t', $args );
  61. $args = array_diff($args, array('-t') );
  62. //$sb = new Socket_Beanstalk( $sbconfig );
  63. $sb = new Socket_Beanstalk( );
  64. if (!$sb->connect()) {
  65. if ($verbose) {
  66. print "Initial connect failed\n";
  67. }
  68. // Normally we'd give up if we can't connect, but in manage mode we'll keep trying every second until beanstalkd comes up.
  69. if (!$manage) {
  70. exit(1);
  71. }
  72. }
  73. if ( $stats ) {
  74. var_dump($sb->stats());
  75. exit;
  76. }
  77. if ($tube = array_shift($args)) {
  78. if ($verbose) {
  79. print "Using tube $tube";
  80. }
  81. $untilts = array_shift($args);
  82. }
  83. if ($list_tubes) {
  84. print "List of tubes:\n";
  85. print join("\n",$sb->listTubes());
  86. print "\n";
  87. exit;
  88. }
  89. if ($manage) {
  90. if ($verbose) {
  91. print "In manage\n";
  92. }
  93. $check_after = 15;
  94. $tubes_watched = array();
  95. while(1) {
  96. $tubes = $sb->listTubes();
  97. if (!is_array($tubes)) {
  98. // Beanstalk server has gone away.
  99. if ($verbose) {
  100. print "Error: Could not get list of tubes from beanstalkd. Will disconnect and try again.\n";
  101. }
  102. $sb->disconnect();
  103. if ($sb->connect()) {
  104. $tubes = $sb->listTubes();
  105. }
  106. }
  107. if ($verbose) {
  108. if (is_array($tubes)) {
  109. print "List found ".count($tubes)."\n";
  110. }
  111. }
  112. if ( ( is_array($tubes) ) && ( count($tubes) > 0 ) ) {
  113. foreach($tubes as $tube) {
  114. if ($verbose) {
  115. print "Checking tube $tube\n";
  116. }
  117. /*
  118. We'll go through the active tubes, check if they have a worker process spawned for them, and if they don't, start one.
  119. We'll tell each script to run until the check_after time, then exit once it's finished the task it's working on.
  120. That will avoid the need to keep checking on all the tubes, all the time.
  121. If a worker process dies prematurely for some reason, it will get restarted when the check_after time comes around.
  122. There will be a short window between the time we tell a worker to end and the time when it actually exists, as it has to finish what it's doing.
  123. During that time, we'll just keep checking the process table.
  124. */
  125. $untilts = time() + $check_after;
  126. // Unset any records we have of tubes whose time is up.
  127. if (isset($tubes_watched[$tube])) {
  128. // Time's up, the process should be exiting right about now.
  129. if ( $tubes_watched[$tube] < time() ) {
  130. unset($tubes_watched[$tube]);
  131. }
  132. }
  133. // Should already be running, won't bother to check.
  134. if (isset($tubes_watched[$tube])) {
  135. continue;
  136. }
  137. if (sloodle_is_child_process_running($tube)) {
  138. continue;
  139. }
  140. if ($verbose) {
  141. print "No child process found for tube $tube - spawning\n";
  142. }
  143. if (sloodle_spawn_child_process($tube, $untilts)) {
  144. // Make a note of the tube we spawned a worker for so we don't have to keep checking.
  145. $tubes_watched[$tube] = $untilts;
  146. if ($verbose) {
  147. print "Spawned for tube $tube \n";
  148. }
  149. }
  150. }
  151. }
  152. sleep(1);
  153. }
  154. }
  155. if ($tube) {
  156. $sb->watch($tube);
  157. sloodle_job_loop($sb, $verbose, $untilts);
  158. exit;
  159. }
  160. //var_dump($sb->stats());
  161. //exit;
  162. while (true) {
  163. $tubes = $sb->listTubes();
  164. if ($verbose) {
  165. //print "Listing tubes\n";
  166. }
  167. if (count($tubes) > 0) {
  168. foreach($tubes as $tube) {
  169. //print "Watching tube $tube\n";
  170. //print "inspecting tube $tube\n";
  171. /*
  172. if (!$sb->choose($tube)) {
  173. print "error: choose failed for tube $tube\n";
  174. }
  175. */
  176. $sb->watch($tube);
  177. }
  178. sloodle_job_loop($sb, $verbose, $untilts);
  179. //!$pid = $sb->put(1000, 0, 10, "hello");
  180. //print "Put job with pid $pid\n";
  181. //var_dump($sb->peekReady());
  182. }
  183. //sleep(1);
  184. }
  185. /*
  186. $tubes = $sb->listTubes();
  187. foreach($tubes as $tube) {
  188. $sb->
  189. }
  190. */
  191. //var_dump($sb->stats());
  192. exit;
  193. function sloodle_job_loop($sb, $verbose, $untilts) {
  194. $timeout = 10;
  195. while( $untilts > time() ) {
  196. if ( $job = $sb->reserve($timeout) ) {
  197. $msg = $job['body'];
  198. $id = $job['id'];
  199. if ($verbose) {
  200. print "Handling job $id\n";
  201. }
  202. if (sloodle_handle_message($msg)) {
  203. if ($verbose) {
  204. print "Deleting job $id\n";
  205. }
  206. $sb->delete($id);
  207. }
  208. }
  209. //sloodle_handle_message($sb, $job, $tube);
  210. }
  211. }
  212. function sloodle_handle_message($msg) {
  213. $lines = explode("\n", $msg);
  214. $statusline = array_shift($lines);
  215. $body = implode("\n", $lines);
  216. //print $url."\n";
  217. if (!$statusline) {
  218. return false;
  219. }
  220. $bits = explode("|", $statusline);
  221. // We should at least have the task and address
  222. if (count($bits) < 2) {
  223. return false;
  224. }
  225. $handler_prefix = $bits[0];
  226. $address = $bits[1];
  227. if (!preg_match("/^[A-Za-z0-9_-]+$/", $handler_prefix)) {
  228. print "handler_prefix $handler_prefix fails regex";
  229. return false;
  230. }
  231. $request_handler = $handler_prefix.'_request.php';
  232. $response_handler = $handler_prefix.'_response.php';
  233. include(SLOODLED_BASE_DIR.'/lib/message_handlers/'.$request_handler);
  234. if (file_exists(SLOODLED_BASE_DIR.'/lib/message_handlers/'.$response_handler)) {
  235. include(SLOODLED_BASE_DIR.'/lib/message_handlers/'.$response_handler);
  236. }
  237. return true;
  238. /*
  239. if (preg_match('/sloodle-(.*?)-(.*)$/', $tube)) {
  240. $task = $matches[1];
  241. $url = $matches[1];
  242. }
  243. print $task;
  244. print $url;
  245. */
  246. return;
  247. $ch = curl_init(); // initialize curl handle
  248. curl_setopt($ch, CURLOPT_URL, $this->httpinurl ); // set url to post to
  249. curl_setopt($ch, CURLOPT_FAILONERROR,0);
  250. curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable
  251. curl_setopt($ch, CURLOPT_TIMEOUT, 10); // times out after 4s
  252. curl_setopt($ch, CURLOPT_POST, 1); // set POST method
  253. curl_setopt($ch, CURLOPT_POSTFIELDS,$msg); // add POST fields
  254. if ($proxy = $this->httpProxyURL()) {
  255. curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
  256. curl_setopt($ch, CURLOPT_PROXY, $this->httpProxyURL() );
  257. }
  258. $result = curl_exec($ch); // run the whole process
  259. $info = curl_getinfo($ch);
  260. curl_close($ch);
  261. return array('info'=>$info,'result'=>$result);
  262. print "ok";
  263. }
  264. function sloodle_is_child_process_running($tube) {
  265. $cmd = "ps aux | grep ".escapeshellarg($tube).' | grep -v grep | wc -l';
  266. //print $cmd."\n";
  267. exec($cmd, $result);
  268. return ( isset($result[0]) && $result[0] > 0 );
  269. }
  270. function sloodle_spawn_child_process($tube, $untilts) {
  271. // The tube may have the site name prefixed to it.
  272. $cmd = '';
  273. if (preg_match('/^(.*?)-.*/', $tube, $matches)) {
  274. $path = SLOODLE_MESSAGE_QUEUE_SITE_PATH_PREFIX.$matches[1].SLOODLE_MESSAGE_QUEUE_SITE_PATH_SUFFIX;
  275. $cmd = 'cd '.$path.' && ';
  276. }
  277. $cmd .= "nohup php sloodled.php ".escapeshellarg($tube).' '.$untilts.' > /dev/null 2>&1 &';
  278. //$cmd .= "php sloodled.php ".escapeshellarg($tube);
  279. print $cmd."\n";
  280. exec($cmd, $result);
  281. // var_dump($result);
  282. return true;
  283. }
  284. exit();
  285. ?>