1
0

Wrapper.wget.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. <?php
  2. /* $Id: Wrapper.wget.php 3237 2007-10-26 21:17:22Z munk $ */
  3. /*******************************************************************************
  4. LICENSE
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License (GPL)
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. To read the license please visit http://www.gnu.org/copyleft/gpl.html
  14. *******************************************************************************/
  15. /**
  16. * class Wrapper for wget-client
  17. */
  18. class WrapperWget
  19. {
  20. // private fields
  21. // vars from args
  22. var $_transfer = "";
  23. var $_owner = "";
  24. var $_path = "";
  25. var $_drate = 0;
  26. var $_retries = 0;
  27. var $_pasv = 0;
  28. var $_commandFile = "";
  29. // runtime-vars
  30. var $_percent_done = 0;
  31. var $_time_left = "-";
  32. var $_down_speed = "0.00 kB/s";
  33. var $_downtotal = 0;
  34. var $_size = 0;
  35. // speed as number
  36. var $_speed = 0;
  37. // pid
  38. var $_pid = 0;
  39. // buffer
  40. var $_buffer = "";
  41. // done-flag
  42. var $_done = false;
  43. // statfile-object-instance
  44. var $_sf = null;
  45. // process-handle
  46. var $_wget = null;
  47. // =========================================================================
  48. // public static methods
  49. // =========================================================================
  50. /**
  51. * start
  52. *
  53. * @param $file
  54. * @param $owner
  55. * @param $path
  56. * @param $drate
  57. * @param $retries
  58. * @param $pasv
  59. */
  60. function start($file, $owner, $path, $drate, $retries, $pasv) {
  61. global $instanceWrapperWget;
  62. $instanceWrapperWget = new WrapperWget($file, $owner, $path, $drate, $retries, $pasv);
  63. $instanceWrapperWget->instance_start();
  64. }
  65. /**
  66. * stop
  67. */
  68. function stop() {
  69. global $instanceWrapperWget;
  70. if (isset($instanceWrapperWget))
  71. $instanceWrapperWget->instance_stop();
  72. }
  73. // =========================================================================
  74. // ctor
  75. // =========================================================================
  76. /**
  77. * ctor
  78. *
  79. * @param $file
  80. * @param $owner
  81. * @param $path
  82. * @param $drate
  83. * @param $retries
  84. * @param $pasv
  85. * @return WrapperWget
  86. */
  87. function WrapperWget($file, $owner, $path, $drate, $retries, $pasv) {
  88. global $cfg;
  89. // set fields from params
  90. $this->_transfer = str_replace($cfg['transfer_file_path'], '', $file);
  91. $this->_owner = $owner;
  92. $this->_path = $path;
  93. $this->_drate = $drate;
  94. $this->_retries = $retries;
  95. $this->_pasv = $pasv;
  96. $this->_commandFile = $file.".cmd";
  97. // set user-var
  98. $cfg["user"] = $this->_owner;
  99. // set admin-var
  100. $cfg['isAdmin'] = IsAdmin($this->_owner);
  101. // init sf-instance
  102. $this->_sf = new StatFile($this->_transfer, $this->_owner);
  103. }
  104. // =========================================================================
  105. // public methods
  106. // =========================================================================
  107. /**
  108. * instance_start
  109. *
  110. * @return boolean
  111. */
  112. function instance_start() {
  113. // wrapper start
  114. $this->_wrapperStart();
  115. // wrapper main
  116. $mainRet = $this->_wrapperMain();
  117. // wrapper stop
  118. $this->_wrapperStop(!$mainRet);
  119. }
  120. /**
  121. * instance_stop
  122. */
  123. function instance_stop() {
  124. // wrapper stop
  125. $this->_wrapperStop(false);
  126. }
  127. // =========================================================================
  128. // private methods
  129. // =========================================================================
  130. /**
  131. * start wrapper
  132. *
  133. * @return boolean
  134. */
  135. function _wrapperStart() {
  136. global $cfg;
  137. // print startup
  138. $this->_outputMessage("wget-wrapper starting up :\n");
  139. $this->_outputMessage(" - transfer : ".$this->_transfer."\n");
  140. $this->_outputMessage(" - owner : ".$this->_owner."\n");
  141. $this->_outputMessage(" - path : ".$this->_path."\n");
  142. $this->_outputMessage(" - drate : ".$this->_drate."\n");
  143. $this->_outputMessage(" - retries : ".$this->_retries."\n");
  144. $this->_outputMessage(" - pasv : ".$this->_pasv."\n");
  145. // write stat-file
  146. $this->_statStartup();
  147. // start client
  148. if (!$this->_clientStart()) {
  149. // stop
  150. $this->_wrapperStop(true);
  151. // return
  152. return false;
  153. }
  154. // signal-handler
  155. if (function_exists("pcntl_signal")) {
  156. $this->_outputMessage("setting up signal-handler...\n");
  157. pcntl_signal(SIGHUP, array($this, "_sigHandler"));
  158. pcntl_signal(SIGINT, array($this, "_sigHandler"));
  159. pcntl_signal(SIGTERM, array($this, "_sigHandler"));
  160. pcntl_signal(SIGQUIT, array($this, "_sigHandler"));
  161. }
  162. // remove command-file if exists
  163. if (@is_file($this->_commandFile)) {
  164. // print
  165. $this->_outputMessage("removing command-file ".$this->_commandFile."...\n");
  166. // delete command-file
  167. @unlink($this->_commandFile);
  168. }
  169. // return
  170. return true;
  171. }
  172. /**
  173. * wrapper main
  174. *
  175. * @return boolean
  176. */
  177. function _wrapperMain() {
  178. // print
  179. $this->_outputMessage("wget up and running\n");
  180. // write stat with "Connecting..."
  181. $this->_statRunning($this->_percent_done, "Connecting...", $this->_down_speed, $this->_downtotal);
  182. // process header
  183. if ($this->_processHeader() === false) {
  184. $this->_outputMessage("failed to start download, shutting down... (pid: ".$this->_pid.")\n");
  185. // print buffer
  186. $this->_outputMessage("buffer :\n".$this->_buffer);
  187. return false;
  188. }
  189. // check for client before entering main-loop
  190. if ($this->_clientIsRunning() === false) {
  191. $this->_outputMessage("wget-client not running, shutting down... (pid: ".$this->_pid.")\n");
  192. // print buffer
  193. $this->_outputMessage("buffer :\n".$this->_buffer);
  194. return false;
  195. }
  196. // write stat with "Downloading..."
  197. $this->_statRunning($this->_percent_done, "Downloading...", $this->_down_speed, $this->_downtotal);
  198. // flush buffer
  199. $this->_buffer = "";
  200. // main loop
  201. $this->_outputMessage("downloading...\n");
  202. $tick = 1;
  203. for (;;) {
  204. // read to buffer
  205. if (!@feof($this->_wget))
  206. $this->_buffer .= @fread($this->_wget, 8192);
  207. // process buffer
  208. $this->_done = $this->_processBuffer();
  209. // return if done
  210. if ($this->_done)
  211. return true;
  212. // process Command Stack, return if quit
  213. if ($this->_processCommandStack())
  214. return true;
  215. // write stat-file every 5 secs
  216. if (($tick % 5) == 0)
  217. $this->_statRunning($this->_percent_done, $this->_time_left, $this->_down_speed, $this->_downtotal);
  218. // check if client is still up once a minute
  219. if (($tick % 60) == 0) {
  220. if ($this->_clientIsRunning() === false) {
  221. $this->_outputMessage("wget-client not running, shutting down... (pid: ".$this->_pid.")\n");
  222. return false;
  223. }
  224. }
  225. // check buffer-size, truncate if needed
  226. if (strlen($this->_buffer) > 16384)
  227. $this->_buffer = substr($this->_buffer, -1024);
  228. // sleep 1 second and increment tick-count
  229. sleep(1);
  230. $tick++;
  231. if (($tick <= 0) || ($tick >= 2147483647)) $tick = 1;
  232. }
  233. // return
  234. return true;
  235. }
  236. /**
  237. * stop wrapper
  238. *
  239. * @param $error
  240. */
  241. function _wrapperStop($error = false) {
  242. // output
  243. $this->_outputMessage("wget-wrapper shutting down...\n");
  244. // stop client
  245. $this->_clientStop();
  246. // transfer settings
  247. stopTransferSettings($this->_transfer);
  248. // stat
  249. $this->_statShutdown($error);
  250. // pid
  251. $this->_pidFileDelete();
  252. // output
  253. $this->_outputMessage("wget-wrapper exit.\n");
  254. // exit
  255. exit();
  256. }
  257. /**
  258. * start client
  259. *
  260. * @return boolean
  261. */
  262. function _clientStart() {
  263. global $cfg;
  264. // print startup
  265. $this->_outputMessage("starting up wget-client...\n");
  266. // command-string
  267. $command = "cd ".tfb_shellencode($this->_path).";";
  268. $command .= " HOME=".tfb_shellencode($this->_path)."; export HOME;";
  269. if ($cfg["enable_umask"] != 0)
  270. $command .= " umask 0000;";
  271. if ($cfg["nice_adjust"] != 0)
  272. $command .= " nice -n ".$cfg["nice_adjust"];
  273. $command .= " ".$cfg['bin_wget'];
  274. $command .= " -c";
  275. if (($this->_drate != "") && ($this->_drate != "0"))
  276. $command .= " --limit-rate=" . $this->_drate;
  277. if ($this->_retries != "")
  278. $command .= " -t ".$this->_retries;
  279. if ($this->_pasv == 1)
  280. $command .= " --passive-ftp";
  281. $command .= " -i ".tfb_shellencode($cfg['transfer_file_path'].$this->_transfer);
  282. $command .= " 2>&1"; // direct STDERR to STDOUT
  283. $command .= " & echo $! > ".tfb_shellencode($cfg['transfer_file_path'].$this->_transfer.".pid"); // write pid-file
  284. // print startup
  285. $this->_outputMessage("executing command : \n".$command."\n", true);
  286. // start process
  287. $this->_wget = @popen($command, 'r');
  288. // wait for 0.5 seconds
  289. usleep(500000);
  290. // get + set pid
  291. $this->_pid = getTransferPid($this->_transfer);
  292. // check for error
  293. if (($this->_wget === false) || ($this->_clientIsRunning() === false)) {
  294. // error
  295. $this->_outputError("error starting up wget-client, shutting down...\n");
  296. // return
  297. return false;
  298. }
  299. // output
  300. $this->_outputMessage("wget-client started. (pid: ".$this->_pid.")\n");
  301. // return
  302. return true;
  303. }
  304. /**
  305. * stop client
  306. *
  307. * @return boolean
  308. */
  309. function _clientStop() {
  310. // close handle
  311. if ((!empty($this->_wget)) && (is_resource($this->_wget))) {
  312. $this->_outputMessage("closing process-handle...\n");
  313. @pclose($this->_wget);
  314. }
  315. // try to kill if running
  316. if ($this->_clientIsRunning()) {
  317. // send KILL ("nohup")
  318. $this->_outputMessage("sending KILL to wget-client... (pid: ".$this->_pid.")\n");
  319. posix_kill($this->_pid, SIGKILL);
  320. // wait for 0.25 seconds
  321. usleep(250000);
  322. // check if running
  323. if ($this->_clientIsRunning()) {
  324. // check if running
  325. if ($this->_clientIsRunning()) {
  326. $this->_outputMessage("wget-client still running 0.25 seconds after KILL. (pid: ".$this->_pid.")\n");
  327. return false;
  328. }
  329. }
  330. // output
  331. $this->_outputMessage("wget-client stopped. (pid: ".$this->_pid.")\n");
  332. } else {
  333. // client not running
  334. $this->_outputMessage("wget-client not running. (pid: ".$this->_pid.")\n");
  335. }
  336. // return
  337. return true;
  338. }
  339. /**
  340. * check if client-process is running
  341. *
  342. * @return boolean
  343. */
  344. function _clientIsRunning() {
  345. return (strpos(exec('ps -p '.tfb_shellencode($this->_pid)), $this->_pid) !== false);
  346. }
  347. /**
  348. * process header
  349. *
  350. * @return boolean
  351. */
  352. function _processHeader() {
  353. // output
  354. $this->_outputMessage("starting download...\n");
  355. // flush buffer
  356. $this->_buffer = "";
  357. // read until we find the Length-string which indicates dl-start
  358. $ctr = 0;
  359. while (($ctr < 64) && (!@feof($this->_wget))) {
  360. // read
  361. $this->_buffer .= @fread($this->_wget, 256);
  362. // check for error
  363. if (preg_match("/.*error.*/i", $this->_buffer)) {
  364. $this->_outputError("error in response.\n");
  365. // return
  366. return false;
  367. }
  368. // check for Length
  369. if (preg_match("/.*Length:\s(.+\d)\s(\[|\()/i", $this->_buffer, $matches)) {
  370. // set size
  371. $this->_size = str_replace(',','', $matches[1]);
  372. // set size in stat-file
  373. $this->_sf->size = $this->_size;
  374. // return
  375. return true;
  376. }
  377. // wait for 0.25 seconds
  378. usleep(250000);
  379. // increment counter
  380. $ctr++;
  381. }
  382. // there were problems
  383. $this->_outputMessage("problems when processing header...\n");
  384. // set size from sf
  385. $this->_outputMessage("try to set size from stat-file...\n");
  386. if (!empty($this->_sf->size)) {
  387. $this->_outputMessage("set size from stat-file :".formatBytesTokBMBGBTB($this->_sf->size)."\n");
  388. $this->_size = $this->_sf->size;
  389. // return
  390. return true;
  391. }
  392. // give up, then we got no size
  393. $this->_outputError("failed to get size for download.\n");
  394. // set size to 0
  395. $this->_size = 0;
  396. // set size in stat-file
  397. $this->_sf->size = $this->_size;
  398. // return
  399. return false;
  400. }
  401. /**
  402. * process buffer
  403. *
  404. * @return boolean
  405. */
  406. function _processBuffer() {
  407. // downtotal
  408. if (preg_match_all("/(\d*)K\s\./i", $this->_buffer, $matches, PREG_SET_ORDER))
  409. $this->_downtotal = $matches[count($matches) - 1][1] * 1024;
  410. // percent_done + down_speed + _speed
  411. if (preg_match_all("/(\d*)%(\s*)(.*)\/s/i", $this->_buffer, $matches, PREG_SET_ORDER)) {
  412. $matchIdx = count($matches) - 1;
  413. // percentage
  414. $this->_percent_done = $matches[$matchIdx][1];
  415. // speed
  416. $this->_down_speed = $matches[$matchIdx][3]."/s";
  417. // we dont want upper-case k
  418. $this->_down_speed = str_replace("KB/s", "kB/s", $this->_down_speed);
  419. // size as int + convert MB/s
  420. $sizeTemp = substr($this->_down_speed, 0, -5);
  421. if (is_numeric($sizeTemp)) {
  422. $this->_speed = intval($sizeTemp);
  423. if (substr($this->_down_speed, -4) == "MB/s") {
  424. $this->_speed = $this->_speed * 1024;
  425. $this->_down_speed = $this->_speed." kB/s";
  426. }
  427. } else {
  428. $this->_speed = 0;
  429. $this->_down_speed = "0.00 kB/s";
  430. }
  431. }
  432. // time left
  433. $this->_time_left = (($this->_size > 0) && ($this->_speed > 0))
  434. ? convertTime((($this->_size - $this->_downtotal) / 1024) / $this->_speed)
  435. : '-';
  436. // download done
  437. if (preg_match("/.*saved\s\[.*/", $this->_buffer)) {
  438. $this->_outputMessage("download complete. initializing shutdown...\n");
  439. // return
  440. return true;
  441. }
  442. // return
  443. return false;
  444. }
  445. /**
  446. * process command stack
  447. *
  448. * @return boolean
  449. */
  450. function _processCommandStack() {
  451. // check for command-file
  452. if (@is_file($this->_commandFile)) {
  453. // print
  454. $this->_outputMessage("processing command-file ".$this->_commandFile."...\n");
  455. // read command-file
  456. $data = @file_get_contents($this->_commandFile);
  457. // delete command-file
  458. @unlink($this->_commandFile);
  459. // process content
  460. $commands = @explode("\n", $data);
  461. if ((is_array($commands)) && (count($commands > 0))) {
  462. foreach ($commands as $command) {
  463. // exec, early out when reading a quit-command
  464. $command = str_replace("\n", "", $command);
  465. $command = trim($command);
  466. if ($this->_execCommand($command)) {
  467. // return
  468. return true;
  469. }
  470. }
  471. } else {
  472. // no commands found
  473. $this->_outputMessage("No commands found.\n");
  474. }
  475. }
  476. // return
  477. return false;
  478. }
  479. /**
  480. * exec a command
  481. *
  482. * @param $command
  483. * @return boolean
  484. */
  485. function _execCommand($command) {
  486. // parse command-string
  487. $len = strlen($command);
  488. if ($len < 1)
  489. return false;
  490. $opcode = $command{0};
  491. $workload = ($len > 1)
  492. ? substr($command, 1)
  493. : "";
  494. // opcode-switch
  495. switch ($opcode) {
  496. // q
  497. case 'q':
  498. $this->_outputMessage("command: stop-request, initializing shutdown...\n");
  499. return true;
  500. // default
  501. default:
  502. $this->_outputMessage("op-code unknown: ".$opcode."\n");
  503. return false;
  504. }
  505. }
  506. /**
  507. * stat-file at startup
  508. *
  509. * @return boolean
  510. */
  511. function _statStartup() {
  512. // set some values
  513. $this->_sf->running = 1;
  514. $this->_sf->percent_done = 0;
  515. $this->_sf->time_left = "Starting...";
  516. $this->_sf->down_speed = "0.00 kB/s";
  517. $this->_sf->up_speed = "0.00 kB/s";
  518. $this->_sf->transferowner = $this->_owner;
  519. $this->_sf->seeds = 1;
  520. $this->_sf->peers = 1;
  521. $this->_sf->sharing = "";
  522. $this->_sf->seedlimit = "";
  523. $this->_sf->uptotal = 0;
  524. $this->_sf->downtotal = 0;
  525. // write
  526. return $this->_sf->write();
  527. }
  528. /**
  529. * stat-file while running
  530. *
  531. * @param $percent_done
  532. * @param $time_left
  533. * @param $down_speed
  534. * @param $downtotal
  535. * @return boolean
  536. */
  537. function _statRunning($percent_done, $time_left, $down_speed, $downtotal) {
  538. // set some values
  539. $this->_sf->percent_done = $percent_done;
  540. $this->_sf->time_left = $time_left;
  541. $this->_sf->down_speed = $down_speed;
  542. $this->_sf->downtotal = $downtotal;
  543. // write
  544. return $this->_sf->write();
  545. }
  546. /**
  547. * stat-file at shutdown
  548. *
  549. * @param $error
  550. * @return boolean
  551. */
  552. function _statShutdown($error = false) {
  553. // set some values
  554. $this->_sf->running = 0;
  555. if ($this->_done) {
  556. $this->_sf->percent_done = 100;
  557. $this->_sf->time_left = "Download Succeeded!";
  558. } else {
  559. $this->_sf->percent_done = ($this->_size > 0)
  560. ? (((intval((100.0 * $this->_downtotal / $this->_size))) + 100) * (-1))
  561. : "-100";
  562. $this->_sf->time_left = "Transfer Stopped";
  563. }
  564. if ($error)
  565. $this->_sf->time_left = "Error";
  566. $this->_sf->down_speed = "";
  567. $this->_sf->up_speed = "";
  568. $this->_sf->transferowner = $this->_owner;
  569. $this->_sf->seeds = "";
  570. $this->_sf->peers = "";
  571. $this->_sf->sharing = "";
  572. $this->_sf->seedlimit = "";
  573. $this->_sf->uptotal = 0;
  574. $this->_sf->downtotal = $this->_downtotal;
  575. // write
  576. return $this->_sf->write();
  577. }
  578. /**
  579. * delete the pid-file
  580. */
  581. function _pidFileDelete() {
  582. global $cfg;
  583. if (@file_exists($cfg['transfer_file_path'].$this->_transfer.".pid")) {
  584. $this->_outputMessage("removing pid-file : ".$cfg['transfer_file_path'].$this->_transfer.".pid\n");
  585. @unlink($cfg['transfer_file_path'].$this->_transfer.".pid");
  586. }
  587. }
  588. /**
  589. * signal-handler
  590. *
  591. * @param $signal
  592. */
  593. function _sigHandler($signal) {
  594. switch ($signal) {
  595. // HUP
  596. case SIGHUP:
  597. $this->_outputMessage("got SIGHUP, ignoring...\n");
  598. break;
  599. // INT
  600. case SIGINT:
  601. $this->_outputMessage("got SIGINT, shutting down...\n");
  602. $this->_wrapperStop(false);
  603. break;
  604. // TERM
  605. case SIGTERM:
  606. $this->_outputMessage("got SIGTERM, shutting down...\n");
  607. $this->_wrapperStop(false);
  608. break;
  609. // QUIT
  610. case SIGQUIT:
  611. $this->_outputMessage("got SIGQUIT, shutting down...\n");
  612. $this->_wrapperStop(false);
  613. break;
  614. }
  615. }
  616. /**
  617. * output message
  618. *
  619. * @param $message
  620. */
  621. function _outputMessage($message) {
  622. @fwrite(STDOUT, @date("[Y/m/d - H:i:s]")." ".$message);
  623. }
  624. /**
  625. * output error
  626. *
  627. * @param $message
  628. */
  629. function _outputError($message) {
  630. @fwrite(STDERR, @date("[Y/m/d - H:i:s]")." ".$message);
  631. }
  632. }
  633. ?>