mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 01:08:56 +00:00
update import.php in misc/testing folder. Remove scripts and classes not used in newznab-tmux.
This commit is contained in:
@@ -1,494 +0,0 @@
|
||||
<?php
|
||||
//
|
||||
// This script is allows you to perform post blacklist scanning
|
||||
// Read about the flags below before using this script.
|
||||
//
|
||||
// The intent of this script is to try and eliminate garbage
|
||||
// entries.
|
||||
//
|
||||
// by l2g
|
||||
//
|
||||
require_once dirname(__FILE__) . '/../../../www/config.php';
|
||||
|
||||
use newznab\db\Settings;
|
||||
|
||||
|
||||
# If satisfied with what is matched, set this to true and have
|
||||
# matched content removed.
|
||||
# This is a DANGEROUS switch to change. Be sure your absolutely
|
||||
# satisfied with the regular expressions being matched are bad.
|
||||
# You'd ideally want to change this flag back to false when you
|
||||
# resume testing.
|
||||
$purgeMatched=false;
|
||||
|
||||
# When this flag is set to true, the database blacklist is applied
|
||||
# to your existing nn db. If you set this to false, then the
|
||||
# blacklist array below is applied instead
|
||||
$use_NNRegexDB=false;
|
||||
|
||||
# By default it looks at all the headers in the database, but sometimes
|
||||
# if your database is just too big and you want to expermeint with different
|
||||
# regular expressions, you can set this flag to false, and instead start
|
||||
# playing with the $res table below labled as Test DB data
|
||||
$use_NNReleasesDB=true;
|
||||
|
||||
# Categorized content can be futher subjected to size restrictions 'IF'
|
||||
# your database is configured for such.
|
||||
# This allows you to filter out crap that makes no sense being
|
||||
# idenified as the category it is based on the category sizing
|
||||
# Note: This is expermental at this point and requires an update to the DB to
|
||||
# work correctly.
|
||||
#
|
||||
# ALTER TABLE `category` ADD `minsize` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `status` ,
|
||||
# ADD `maxsize` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `minsize` ;
|
||||
#
|
||||
# ;; Update tvshows.SD to set a min value of 30MB (in bytes)
|
||||
# UPDATE `newznab`.`category` SET `minsize` = '31457280' WHERE `category`.`ID` =5030 LIMIT 1 ;
|
||||
# ;; Update movies.SD to set a min value of 50MB (in bytes)
|
||||
# UPDATE `newznab`.`category` SET `minsize` = '52428800' WHERE `category`.`ID` =2030 LIMIT 1 ;
|
||||
#
|
||||
# This also requires a few functions in the lib/category.php
|
||||
# Better just wait before enabling this :)
|
||||
$use_CategorySizeRestrictions=true;
|
||||
# This flag is only used if $use_CategorySizeRestrictions (above) is set to true.
|
||||
# setting this value to false, will have all category restrictions defined in the
|
||||
# static table below take effect... otherwise, the script will query the database (it's assumed
|
||||
# that all the proposed changes were accepted and pushed into the DB). Leave this value to
|
||||
# false until it is certain the database changes have been pushed. When these chagnes are pushed
|
||||
# this flag will be merged with the above.
|
||||
$use_NNCategorySizeRestrictions=false;
|
||||
|
||||
# Completion Restriction allows you to remove content that is older then
|
||||
# X hrs and is less then 'Y%' complete.
|
||||
# Why keep content in the database that can't be put back together
|
||||
$use_CompletionRestrictions=true;
|
||||
# The precentage *must be less then (or equal to) 100* to accept 'up to'
|
||||
$completionAllowable=90;
|
||||
# The number of hours the release has aged and still hasn't met the allowable
|
||||
# completion percentage (in seconds) 10800 is equal to 3 hrs
|
||||
$completionSec=10800;
|
||||
|
||||
# check or the article is considered bogus. This greatly increasese
|
||||
# the processing time against the database.
|
||||
# Be warned that if your cataloging music, almost every band out there
|
||||
# spells their name with something that does not coincide with a dictionary
|
||||
# value.
|
||||
$use_spellCheck=false;
|
||||
|
||||
# Identify the number of dictionaries you want to check the spelling
|
||||
# against. International spell checking might include 'ge', '
|
||||
$spellCheckLang=array('en');
|
||||
|
||||
# International (uncomment the below)
|
||||
#$spellCheckLang=array('en', 'fr', 'it', 'ge', 'es', 'nl');
|
||||
|
||||
# Exceptions are bad spellings that exist in a filename but still
|
||||
# mark it valid; This list is not case sensitive, so add any
|
||||
# keywords that should not be considered while searching
|
||||
$spellExceptions=array(
|
||||
# Some good tokens
|
||||
'xvid', 'DVD', 'DVDRip', 'Bluray', 'x264', '480p', '720p', '1080p',
|
||||
'HDRIP',
|
||||
# TV Episode Tags
|
||||
'S[0-9]{1,2}([. -]*E[0-9]{1,2}$|$)',
|
||||
|
||||
# Tv Series release groups
|
||||
"BARC0DE", "DIMENSION", "LOL", "WEB-DL", "SUBLiME", "2HD", "FMQ",
|
||||
"BIA",
|
||||
|
||||
# MP3/FLAC Groups
|
||||
"FiH", "SDR",
|
||||
|
||||
# Software Groups
|
||||
"LAXiTY",
|
||||
);
|
||||
|
||||
# Some default category restrictions to play with for those who haven't
|
||||
# updated their database yet and would still like size restrictions
|
||||
# in place.
|
||||
$categoryRestrictions=array(
|
||||
# TV Standard Def (SD) set a 30MB Min restriction
|
||||
Category::CAT_TV_SD=>array('min'=>31457280, 'max'=>PHP_INT_MAX),
|
||||
# TV High Def (HD) set a 40MB Min restriction
|
||||
Category::CAT_TV_HD=>array('min'=>41943040, 'max'=>PHP_INT_MAX),
|
||||
# Movies Standard Def (SD) set a 50MB Min restriction
|
||||
Category::CAT_MOVIE_SD=>array('min'=>52428800, 'max'=>PHP_INT_MAX),
|
||||
# Movies High Def (HD) set a 80MB Min restriction
|
||||
Category::CAT_MOVIE_HD=>array('min'=>83886080, 'max'=>PHP_INT_MAX),
|
||||
# Movies Other set a 30MB Min restriction
|
||||
Category::CAT_MOVIE_OTHER=>array('min'=>31457280, 'max'=>PHP_INT_MAX),
|
||||
# Bluray movies, 350MB Min restriction
|
||||
Category::CAT_MOVIE_BLURAY=>array('min'=>367001600, 'max'=>PHP_INT_MAX),
|
||||
);
|
||||
|
||||
# Throttle cleanup/searching by performing batches... the larger
|
||||
# the number, the faster processing will go, but will demand more
|
||||
# system memory...
|
||||
$batch=500;
|
||||
|
||||
# Throttle
|
||||
# The throttle allows a scan of your database without impacting performance
|
||||
# to the public who may or may not be using your database. it also reduces
|
||||
# the cpu demand of the cleanup. Set this value to 0 if your system is
|
||||
# private and dedicated to yourself for the best speed. It identifies
|
||||
# the number of seconds to sleep for between each batch
|
||||
$throttle=0.5;
|
||||
|
||||
# This blacklist is an aray of regular expressions that you can use
|
||||
# to test with (sometimes it's easier to update a table infront of
|
||||
# you as a developer then going back and making a database change
|
||||
# and then returning to run the script.
|
||||
$blacklist = array(
|
||||
# random string of crap
|
||||
array("groupname"=>"alt.binaries.*", "regex"=>'^[a-z0-9]{1,80}([0-9-]+$|$)'),
|
||||
# Missing blacklist catching of foreign content
|
||||
array("groupname"=>"alt.binaries.*", "regex"=>'[-.](FR|DE|ITA)[-.]'),
|
||||
# Common German Keywords
|
||||
array("groupname"=>"alt.binaries.*", "regex"=>'(^|[.\/ \-]+)(ohne|das|der|und|fuer|ersten|leicht|meinem|zum|aus|dem|blitzlicht|alle|grosse|blitzen|ich|zed|sed)([.\/ \-]+|$)'),
|
||||
# random string of crap in alt.binaries.sounds.mp3, and alt.binaries.multimedia
|
||||
array("groupname"=>"alt.binaries.*", "regex"=>'^[a-z0-9]{5} [0-9]{8} [0-9]{3}$'),
|
||||
# random crap like: acef826f-6c8b-405c-b7f2-c9c143d726a8
|
||||
array("groupname"=>"alt.binaries.*", "regex"=>'^[a-z0-9]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$'),
|
||||
array("groupname"=>"alt.binaries.*", "regex"=>'^[a-z0-9.]+\.BDR$'),
|
||||
array("groupname"=>"alt.binaries.*", "regex"=>'^\([0-9.]+\)$'),
|
||||
|
||||
# Password protected content that was marked by the poster
|
||||
array("groupname"=>"alt.binaries.*", "regex"=>'\[password\]'),
|
||||
);
|
||||
|
||||
# Test DB Data; if you plan on using this, you need to set the $use_NNReleasesDB to false
|
||||
$test_headers = array(
|
||||
# Set id's to 0 so id's are purged unessisarily when testing
|
||||
array('ID'=>0, 'groupname'=>"alt.binaries.multimedia", 'name'=>"1204odayrtgh6j7app"),
|
||||
array('ID'=>0, 'groupname'=>"alt.binaries.multimedia", 'name'=>"BB555"),
|
||||
array('ID'=>0, 'groupname'=>"alt.binaries.multimedia", 'name'=>"bnedhe8utrh5tnbg9"),
|
||||
array('ID'=>0, 'groupname'=>"alt.binaries.multimedia", 'name'=>"Uoaaqunio-396653289-201212201118"),
|
||||
array('ID'=>0, 'groupname'=>"alt.binaries.multimedia", 'name'=>"acef826f-6c8b-405c-b7f2-c9c143d726a8"),
|
||||
array('ID'=>0, 'groupname'=>"alt.binaries.multimedia", 'name'=>"ich26389"),
|
||||
array('ID'=>0, 'groupname'=>"alt.binaries.multimedia", 'name'=>"GkTNPvg3"),
|
||||
array('ID'=>0, 'groupname'=>"alt.binaries.multimedia", 'name'=>"RVL 25 12 2012 G O M S BDR"),
|
||||
array('ID'=>0, 'groupname'=>"alt.binaries.multimedia", 'name'=>"25 12 2012 M S R BDR"),
|
||||
array('ID'=>0, 'groupname'=>"alt.binaries.multimedia", 'name'=>"23YKC 20121212 013"),
|
||||
);
|
||||
|
||||
function handleError($errno, $errstr, $errfile, $errline, array $errcontext)
|
||||
{
|
||||
// this function makes it easier to catch regular expression errors
|
||||
// found
|
||||
if (0 === error_reporting()) {
|
||||
return false;
|
||||
}
|
||||
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
|
||||
}
|
||||
set_error_handler('handleError');
|
||||
|
||||
function get_block($offset, $batch)
|
||||
{
|
||||
global $use_NNReleasesDB;
|
||||
static $retrieved=false;
|
||||
if(!$use_NNReleasesDB){
|
||||
// use a retrieved boolean for test data so content
|
||||
// is only fetched once.
|
||||
if(!$retrieved && $offset > 0){
|
||||
return Null;
|
||||
}
|
||||
global $test_headers;
|
||||
// toggle retrieved content on test data
|
||||
$retrieved=true;
|
||||
return $test_headers;
|
||||
}
|
||||
|
||||
$catsql = "SELECT releases.*,groups.name as groupname FROM "
|
||||
."releases LEFT JOIN groups on releases.groupID = groups.ID ";
|
||||
$db = new Settings();
|
||||
return $db->query($catsql.sprintf(' LIMIT %d,%d', $offset, $batch));
|
||||
}
|
||||
|
||||
$db = new Settings();
|
||||
|
||||
$errcnt=0;
|
||||
$total=0;
|
||||
$offset=0;
|
||||
|
||||
# Dictionary
|
||||
$pspelldict=[];
|
||||
|
||||
# Completion Refernece Time in past
|
||||
$completionRef = strtotime("-$completionSec seconds");
|
||||
|
||||
# Scan header for spelling...; at least 1 word has to match the spelling
|
||||
if($use_spellCheck && !function_exists('pspell_check')){
|
||||
echo "Note: Disabling spell checking as it is not available in your PHP installation.\n";
|
||||
echo " Consider the following to enable it:";
|
||||
echo " ubuntu #> sudo apt-get install php5-pspell\n";
|
||||
echo " redhat #> yum install php-pspell\n";
|
||||
echo " other #> # re-compile php with --with-pspell flag\n";
|
||||
$use_spellCheck=false;
|
||||
}
|
||||
|
||||
$binaries = new Binaries();
|
||||
if($use_NNRegexDB)
|
||||
# Over-ride Blacklist up top and use database
|
||||
$blacklist = $binaries->getBlacklist(true);
|
||||
|
||||
while($res=get_block($offset, $batch)){
|
||||
|
||||
$subtotal=count($res);
|
||||
$total+=$subtotal;
|
||||
|
||||
$release = new Releases();
|
||||
$category = new Category();
|
||||
|
||||
// Track error count changes
|
||||
$_errcnt=$errcnt;
|
||||
foreach ($res as $header)
|
||||
{
|
||||
// if a white list is detected we now are required to
|
||||
// only accept the entry if it matches at least 1 whitelist
|
||||
// while a blacklist will over-ride all
|
||||
$whitelist = [];
|
||||
$matches_whitelist = false;
|
||||
|
||||
foreach ($blacklist as $bl)
|
||||
{
|
||||
// opttype = 1 -> Blacklist (Default)
|
||||
// opttype = 2 -> Whitelist
|
||||
$opttype = Binaries::OPTYPE_BLACKLIST;
|
||||
if (array_key_exists ('opttype' , $bl)){
|
||||
$opttype = intval($bl['opttype']);
|
||||
}
|
||||
|
||||
// Regular expression
|
||||
$regex = $bl['regex'];
|
||||
|
||||
// Default msgcol is subject
|
||||
$msgcol = Binaries::BLACKLIST_FIELD_SUBJECT;
|
||||
$parse = $header['name'];
|
||||
if (array_key_exists ('msgcol' , $bl)){
|
||||
$msgcol = intval($bl['msgcol']);
|
||||
}
|
||||
|
||||
switch($msgcol)
|
||||
{
|
||||
case Binaries::BLACKLIST_FIELD_FROM:
|
||||
if(!array_key_exists ('fromname' , $bl)){
|
||||
# we only process fromname if we found it... otherwise we keep going
|
||||
continue;
|
||||
}
|
||||
$parse = $header['fromname'];
|
||||
break;
|
||||
case Binaries::BLACKLIST_FIELD_MESSAGEID:
|
||||
# unsupported; you can't get this information from an already
|
||||
# populated database
|
||||
continue;
|
||||
};
|
||||
|
||||
if ($opttype == Binaries::OPTYPE_WHITELIST){
|
||||
// at least 1 whitelist found...
|
||||
// this means we have to match it or be rejected
|
||||
$whitelist[] = $regex;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$group_check = preg_match('/^'.$bl['groupname'].'$/i', $header['groupname']);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
# Support letting the user know when there is a bad
|
||||
# regular expression entry abort futher checks
|
||||
$strerr=str_pad((int) $errcnt,2," ",STR_PAD_LEFT);
|
||||
echo "$strerr. id=".$bl["id"].
|
||||
", group=".$header["groupname"]."\n";
|
||||
echo " regex='".$bl["groupname"]."'\n";
|
||||
echo " error=".$e->getMessage()."\n\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ($group_check)
|
||||
{
|
||||
try
|
||||
{
|
||||
$blacklist_check = preg_match('/'.$regex.'/i', $parse);
|
||||
// White lists react in the invert manner... something that
|
||||
// doesn't match causes us to keep going
|
||||
if($opttype == Binaries::OPTYPE_WHITELIST && $blacklist_check){
|
||||
// invert check since it's a good thing we matched
|
||||
$blacklist_check=false;
|
||||
// Flag that we matched the white list
|
||||
$matches_whitelist = true;
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
# Support letting the user know when there is a bad
|
||||
# regular expression entry abort futher checks
|
||||
$strerr=str_pad((int) $errcnt,2," ",STR_PAD_LEFT);
|
||||
if(isset($bl["id"]))
|
||||
echo "$strerr. id=".$bl["id"].", ";
|
||||
echo " name=".$header["name"]."\n";
|
||||
echo " regex=$regex\n";
|
||||
echo " error=".$e->getMessage()."\n\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ($blacklist_check)
|
||||
{
|
||||
$errcnt++;
|
||||
echo 'BL: /'.$bl['regex'].'/i matched '.$header['ID'].'/'.$header['name'].
|
||||
" (".$header['groupname'].")\n";
|
||||
if ($purgeMatched && isset($header['ID']) && $header['ID'] > 0){
|
||||
// Support purging if enabled
|
||||
$release->delete($header['ID']);
|
||||
}
|
||||
# ensure whitelist flags are off to avoid extra processing
|
||||
# at the end of this loop (Below)
|
||||
$whitelist = [];
|
||||
break;
|
||||
}
|
||||
|
||||
if($use_CategorySizeRestrictions == true && $header['categoryID'])
|
||||
{
|
||||
$sizes = null;
|
||||
if($use_NNCategorySizeRestrictions){
|
||||
$sizes = $category->getSizeRangeById($header['categoryID']);
|
||||
}else if (array_key_exists ($header['categoryID'], $categoryRestrictions)){
|
||||
$sizes = $categoryRestrictions[$header['categoryID']];
|
||||
}
|
||||
if(is_array($sizes)){
|
||||
# Restrictions are in place
|
||||
$totalSize=intval($header['size']);
|
||||
if(!($totalSize >= $sizes['min'] && $totalSize < $sizes['max']))
|
||||
{
|
||||
$errcnt++;
|
||||
echo 'BL: size restrictions '.$header['ID'].'/'.$header['name'].
|
||||
" (".$header['groupname'].")\n";
|
||||
if ($purgeMatched && isset($header['ID']) && $header['ID'] > 0){
|
||||
// Support purging if enabled
|
||||
$release->delete($header['ID']);
|
||||
}
|
||||
# ensure whitelist flags are off to avoid extra processing
|
||||
# at the end of this loop (Below)
|
||||
$whitelist = [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($use_CompletionRestrictions == true){
|
||||
$lastUpdate=strtotime($header['updatedate']);
|
||||
$completion=intval($header['completion']);
|
||||
|
||||
if($completion < $completionAllowable && $lastUpdate < $completionRef)
|
||||
{
|
||||
$errcnt++;
|
||||
echo 'BL: incomplete '.$header['ID'].'/'.$header['name'].
|
||||
" (".$header['groupname'].")\n";
|
||||
if ($purgeMatched && isset($header['ID']) && $header['ID'] > 0){
|
||||
// Support purging if enabled
|
||||
$release->delete($header['ID']);
|
||||
}
|
||||
# ensure whitelist flags are off to avoid extra processing
|
||||
# at the end of this loop (Below)
|
||||
$whitelist = [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($use_spellCheck == true &&
|
||||
$opttype == Binaries::OPTYPE_BLACKLIST &&
|
||||
$msgcol == Binaries::BLACKLIST_FIELD_SUBJECT){
|
||||
|
||||
# Track matched
|
||||
$matched=0;
|
||||
# First extract the words
|
||||
$keywords = preg_split('/[^a-z0-9]+/i', $parse);
|
||||
foreach($spellCheckLang as $lang){
|
||||
if (!array_key_exists ($lang , $pspelldict))
|
||||
$pspelldict[$lang] = pspell_new($lang);
|
||||
foreach($keywords as $kw){
|
||||
foreach($spellExceptions as $exception){
|
||||
if(preg_match('/'.$exception.'/i', $kw)){
|
||||
$matched++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($matched>0)break;
|
||||
|
||||
if(pspell_check($pspelldict[$lang], $kw)){
|
||||
$matched++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
# we only need to match once to presume the data
|
||||
# is good
|
||||
if($matched>0)break;
|
||||
}
|
||||
# how did we do for spelling matches
|
||||
if(!$matched){
|
||||
$errcnt++;
|
||||
echo 'BL: due to bad spelling '.$header['ID'].'/'.$header['name'].
|
||||
" (".$header['groupname'].")\n";
|
||||
if ($purgeMatched && isset($header['ID']) && $header['ID'] > 0){
|
||||
// Support purging if enabled
|
||||
$release->delete($header['ID']);
|
||||
}
|
||||
# ensure whitelist flags are off to avoid extra processing
|
||||
# at the end of this loop (Below)
|
||||
$whitelist = [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# We parsed entire matching list entries at this point.. now we need
|
||||
# to handle the whitelist (if it was enabled)
|
||||
if(count($whitelist) > 0 && !$matches_whitelist)
|
||||
{
|
||||
$errcnt++;
|
||||
$first_loop=true;
|
||||
foreach ($whitelist as $entry){
|
||||
if($first_loop)
|
||||
{
|
||||
$first_loop=false;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo '\n';
|
||||
}
|
||||
echo 'WL: /'.$bl['regex'].'/i';
|
||||
}
|
||||
echo ' failed to match '.$header['ID'].'/'.$header['name'].
|
||||
" (".$header['groupname'].")\n";
|
||||
|
||||
if ($purgeMatched && $header['ID'] > 0){
|
||||
// Support purging if enabled
|
||||
$release->delete($header['ID']);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!$purgeMatched || $_errcnt==$errcnt){
|
||||
# No changes... update batch
|
||||
$offset+=$batch;
|
||||
}
|
||||
|
||||
if($subtotal == $batch){
|
||||
// Presume there is more content to come
|
||||
// throttle request
|
||||
if($throttle > 0.0){
|
||||
sleep($throttle);
|
||||
}
|
||||
}
|
||||
}
|
||||
echo "Scanned $total record(s), $errcnt match(es) found.\n";
|
||||
if($errcnt >0){
|
||||
if(!$purgeMatched){
|
||||
echo "Note: This scan was performed in a safe read-only mode.\n";
|
||||
echo " If you are satisfied with the output as being 'garbage/spam'\n".
|
||||
" then consider editing this file and setting the \$purgeMatched flag to 'true'\n";
|
||||
}else{
|
||||
echo "Note: This scan was performed in a write mode; all content matched was removed.\n";
|
||||
}
|
||||
}
|
||||
exit(($errcnt>0)?1:0);
|
||||
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
|
||||
//This script allows you to delete properly all releases which match some criteria
|
||||
//The nzb, covers and all linked records will be deleted properly.
|
||||
|
||||
require_once dirname(__FILE__) . '/../../www/config.php';
|
||||
|
||||
use newznab\db\Settings;
|
||||
|
||||
$releases = new Releases();
|
||||
$db = new Settings();
|
||||
|
||||
//
|
||||
// [1] delete all releases for a group which only has x number of files
|
||||
//
|
||||
//$sql = "select * from releases where totalpart = 1 and groupID in (select ID from groups where name = 'alt.binaries.cd.image')";
|
||||
|
||||
|
||||
//
|
||||
// [2] delete all releases where the only file inside the rars is *.exe and they are not in the PC category
|
||||
//
|
||||
/*
|
||||
$sql = "
|
||||
select releasefiles.releaseID as ID
|
||||
from releasefiles
|
||||
inner join
|
||||
( select releaseID, count(*) as totnum from releasefiles group by releaseID ) x on x.releaseID = releasefiles.releaseID and x.totnum = 1
|
||||
inner join releases on releases.ID = releasefiles.releaseID
|
||||
where releasefiles.name like '%.exe'
|
||||
and releases.categoryID not in (4000,4010,4020,4030,4040,4050)
|
||||
group by releasefiles.releaseID ";
|
||||
*/
|
||||
|
||||
//
|
||||
// [3] delete all releases which contain a file with password.url in its
|
||||
//
|
||||
//$sql = "select distinct releasefiles.releaseID from releasefiles where name = 'password.url'";
|
||||
|
||||
//
|
||||
// [4] delete all releases for a poster
|
||||
//
|
||||
//$sql = "select ID from releases where fromname = 'PowerBUS@gmail.com (PowerBUS)'";
|
||||
|
||||
//
|
||||
// [5] delete all under a certain amount of completion
|
||||
//
|
||||
//$sql = "select ID from releases where completion != 0 and completion != 100 and completion < 99";
|
||||
|
||||
//
|
||||
// [6] all audio which contains a file with .exe or install.bin in
|
||||
//
|
||||
//$sql = "select distinct r.ID from releasefiles rf inner join releases r on r.id = rf.releaseID and r.categoryID like '3%' where rf.name like '%.exe' or rf.name = 'install.bin'";
|
||||
|
||||
//
|
||||
// [7] delete all releases for a name
|
||||
//
|
||||
//$sql = "select ID from releases where searchname like '%Friday The 13th The Series S03 -enjoy-%'";
|
||||
|
||||
//
|
||||
// [8] delete all releases for a regex
|
||||
//
|
||||
//$sql = "select ID from releases where regexID = 1307";
|
||||
|
||||
//
|
||||
// [9] delete all releases under a certain size (100MB) for a category
|
||||
//
|
||||
//$sql = "select ID from releases where categoryID like '2%' and size < 104857600";
|
||||
|
||||
$rel = $db->query($sql);
|
||||
echo "about to delete ".count($rel)." release(s)";
|
||||
|
||||
foreach ($rel as $r)
|
||||
{
|
||||
$releases->delete($r['ID']);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once dirname(__FILE__) . '/../../www/config.php';
|
||||
|
||||
$echoonly = false;
|
||||
$limitedtotoday = true;
|
||||
$verbose = true;
|
||||
|
||||
$p = new Parsing($echoonly, $limitedtotoday, $verbose);
|
||||
$p->removeSpecial();
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__FILE__) . '/../../www/config.php';
|
||||
|
||||
$echoonly = true;
|
||||
$limittotoday = false;
|
||||
$verbose = true;
|
||||
|
||||
$p = new Parsing($echoonly, $limittotoday, $verbose);
|
||||
$p->cleanup();
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__FILE__) . '/../../www/config.php';
|
||||
|
||||
$echoonly = false;
|
||||
$limittotoday = false;
|
||||
$verbose = true;
|
||||
|
||||
$p = new Parsing($echoonly, $limittotoday, $verbose);
|
||||
$p->process();
|
||||
@@ -6,6 +6,7 @@ use newznab\db\Settings;
|
||||
use newznab\utility\Utility;
|
||||
|
||||
$releases = new Releases();
|
||||
$releaseImage = new ReleaseImage();
|
||||
$db = new Settings();
|
||||
$cat = new Categorize();
|
||||
$releaseRegex = new ReleaseRegex();
|
||||
@@ -116,7 +117,7 @@ foreach ($filestoprocess as $nzbFile) {
|
||||
if (!file_exists($errorpath)) mkdir($errorpath);
|
||||
if (!file_exists($errorpath . basename($nzbFile))) rename($nzbFile, $errorpath . basename($nzbFile));
|
||||
}
|
||||
$releases->delete($relid);
|
||||
$releases->deleteSingle(['g' => $relguid, 'i' => $relid], $nzb, $releaseImage);
|
||||
}
|
||||
} else {
|
||||
$numbins = 0;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,808 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(ticks = 1);
|
||||
|
||||
/**
|
||||
* PowerProcess is an abstraction class for PHP's posix and pcntl extensions.
|
||||
*
|
||||
* It enables easy process forking or threading to allow use of parallel
|
||||
* processes for completing complicated tasks that would otherwise be
|
||||
* inefficient for normal serial and procedural processing
|
||||
*
|
||||
* If you like PowerProcess, please consider donating
|
||||
* - BTC: 1K2tvdYzdDDd8w6vNHQgvbNQnhcHqLEadx
|
||||
* - LTC: LfceD3QH2n1FqH8inqHdKxjBFV55QvuESv
|
||||
*
|
||||
* @package PowerProcess
|
||||
*
|
||||
* @author Don Bauer <lordgnu@me.com>
|
||||
* @link https://github.com/lordgnu/PowerProcess
|
||||
* @license MIT License
|
||||
* @version 2.0
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (c) 2011 Don Bauer <lordgnu@me.com>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
class PowerProcess {
|
||||
const CALLBACK_STOP_PROPOGATION = -1;
|
||||
const CALLBACK_IGNORE = 0;
|
||||
const CALLBACK_CONTINUE = 1;
|
||||
const CALLBACK_SHUTDOWN = 2;
|
||||
const CALLBACK_RESTART = 3;
|
||||
|
||||
/**
|
||||
* Current PowerProcess version
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $version = '2.1';
|
||||
|
||||
/**
|
||||
* Data store for data that is to be passed to the child process which is to be spawned
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $threadData;
|
||||
|
||||
/**
|
||||
* Boolean variable which determines whether or not to shutdown the control process (parent)
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $complete;
|
||||
|
||||
/**
|
||||
* Callback array for setting callback functions based on signals that can be sent to the parent process
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $callbacks;
|
||||
|
||||
/**
|
||||
* The name of the current thread. Used by WhoAmI()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $currentThread;
|
||||
|
||||
/**
|
||||
* Whether to log internal debug message
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $debugLogging;
|
||||
|
||||
/**
|
||||
* The maximum number of concurrent threads that can be running at any given time.
|
||||
*
|
||||
* This setting has an impact on performance for PowerProcess so play
|
||||
* with it on the system you are on to determine a good value.
|
||||
* 10 is a good place to start
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $maxThreads;
|
||||
|
||||
/**
|
||||
* Array which stores the thread data for the control process (parent) to manage running child threads
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $myThreads;
|
||||
|
||||
/**
|
||||
* Session id of parent session when process is daemonized
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $parentSID;
|
||||
|
||||
/**
|
||||
* The pid of the parent process
|
||||
*
|
||||
* Used after a process is forked to
|
||||
* determine whether the new thread is to run the thread code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $parentPID;
|
||||
|
||||
/**
|
||||
* Sleep timer in micro seconds for the parent process to sleep between status checks using Tick()
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $tickCount = 100;
|
||||
|
||||
/**
|
||||
* Whether to add a timestamp to log output
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $timeStampLogs = true;
|
||||
|
||||
/**
|
||||
* The maximum number of seconds a thread will be allowed to run.
|
||||
*
|
||||
* Set to 0 to disable a time limit (use with caution)
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $threadTimeLimit;
|
||||
|
||||
/**
|
||||
* Location to log information messages to.
|
||||
*
|
||||
* Can be a file or php://stdout, php://stderr.
|
||||
* Set to false to disable
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $logTo;
|
||||
|
||||
/**
|
||||
* When logging is enabled, this points to the socket in which to write log messages.
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
public $logSocket;
|
||||
|
||||
/**
|
||||
* Signals to install for SignalDispatcher.
|
||||
*
|
||||
* You can use any signal constant PNCTL supports
|
||||
* @link http://us3.php.net/manual/en/pcntl.constants.php
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $signalArray = array(
|
||||
SIGUSR1, // User-Defined 1
|
||||
SIGUSR2 // User-Defined 2
|
||||
);
|
||||
|
||||
static public function SignalName($signal) {
|
||||
switch ($signal) {
|
||||
case SIGHUP:
|
||||
return 'SIGHUP';
|
||||
case SIGINT:
|
||||
return 'SIGINT';
|
||||
case SIGQUIT:
|
||||
return 'SIGQUIT';
|
||||
case SIGILL:
|
||||
return 'SIGILL';
|
||||
case SIGTRAP:
|
||||
return 'SIGTRAP';
|
||||
case SIGABRT:
|
||||
return 'SIGABRT';
|
||||
case SIGIOT:
|
||||
return 'SIGIOT';
|
||||
case SIGBUS:
|
||||
return 'SIGBUS';
|
||||
case SIGFPE:
|
||||
return 'SIGFPE';
|
||||
case SIGUSR1:
|
||||
return 'SIGUSR1';
|
||||
case SIGSEGV:
|
||||
return 'SIGSEGV';
|
||||
case SIGUSR2:
|
||||
return 'SIGUSR2';
|
||||
case SIGPIPE:
|
||||
return 'SIGPIPE';
|
||||
case SIGALRM:
|
||||
return 'SIGALRM';
|
||||
case SIGTERM:
|
||||
return 'SIGTERM';
|
||||
case SIGSTKFLT:
|
||||
return 'SIGSTKFLT';
|
||||
case SIGCLD:
|
||||
return 'SIGCLD';
|
||||
case SIGCHLD:
|
||||
return 'SIGCHLD';
|
||||
case SIGCONT:
|
||||
return 'SIGCONT';
|
||||
case SIGTSTP:
|
||||
return 'SIGTSTP';
|
||||
case SIGTTIN:
|
||||
return 'SIGTTIN';
|
||||
case SIGTTOU:
|
||||
return 'SIGTTOU';
|
||||
case SIGURG:
|
||||
return 'SIGURG';
|
||||
case SIGXCPU:
|
||||
return 'SIGXCPU';
|
||||
case SIGXFSZ:
|
||||
return 'SIGXFSZ';
|
||||
case SIGVTALRM:
|
||||
return 'SIGVTALRM';
|
||||
case SIGPROF:
|
||||
return 'SIGPROF';
|
||||
case SIGWINCH:
|
||||
return 'SIGWINCH';
|
||||
case SIGPOLL:
|
||||
return 'SIGPOLL';
|
||||
case SIGIO:
|
||||
return 'SIGIO';
|
||||
case SIGPWR:
|
||||
return 'SIGPWR';
|
||||
case SIGSYS:
|
||||
return 'SIGSYS';
|
||||
case SIGBABY:
|
||||
return 'SIGBABY';
|
||||
case SIG_BLOCK:
|
||||
return 'SIG_BLOCK';
|
||||
case SIG_UNBLOCK:
|
||||
return 'SIG_UNBLOCK';
|
||||
case SIG_SETMASK:
|
||||
return 'SIG_SETMASK';
|
||||
default:
|
||||
return "Signal # {$signal}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PowerProcess constructor.
|
||||
*
|
||||
* Returns an instanced PowerProcess object or dies on failure
|
||||
*
|
||||
* @param integer $maxThreads Max number of concurrent threads to allow at any given time
|
||||
* @param integer $threadTimeLimit Maximum number of seconds a thread is allowed to live
|
||||
* @param boolean $daemon Whether to start as a deamon or just a normal script
|
||||
* @param string $logTo What stream to log output to
|
||||
* @param boolean $debugLogging Whether to enable debug logging
|
||||
*
|
||||
* @return object Instanced PowerProcess object
|
||||
*/
|
||||
public function __construct($maxThreads = 10, $threadTimeLimit = 300, $daemon = false, $logTo = false, $debugLogging = false) {
|
||||
if (function_exists('pcntl_fork') && function_exists('posix_getpid')) {
|
||||
// Set the current thread name
|
||||
$this->currentThread = 'CONTROL';
|
||||
|
||||
// Set the max threads setting
|
||||
$this->SetMaxThreads($maxThreads);
|
||||
|
||||
// Set the thread time limit setting
|
||||
$this->SetThreadTimeLimit($threadTimeLimit);
|
||||
|
||||
// Init the logger
|
||||
$this->InitializeLogger($logTo, $debugLogging);
|
||||
|
||||
if ($daemon) {
|
||||
// Attempt to daemonize
|
||||
if (!$this->Daemonize()) {
|
||||
die("Could not daemonize");
|
||||
} else {
|
||||
$this->Log("Daemonized successfully",true);
|
||||
}
|
||||
} else {
|
||||
// Register control process PID
|
||||
$this->parentPID = $this->GetPID();
|
||||
$this->parentSID = false;
|
||||
$this->Log("Parent PID detected as {$this->parentPID}",true);
|
||||
}
|
||||
|
||||
// The the complete flag to false
|
||||
$this->complete = false;
|
||||
|
||||
// Install the signal handler
|
||||
$this->InstallSignalHandler();
|
||||
|
||||
// Init the Thread Queue
|
||||
$this->myThreads = [];
|
||||
|
||||
// Log completion of startup
|
||||
$this->Log("Startup process complete",true);
|
||||
} else {
|
||||
die("PowerProcess requires both the POSIX and PCNTL extensions to operate.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees up memory
|
||||
*/
|
||||
public function __destruct() {
|
||||
unset($this->callbacks);
|
||||
unset($this->myThreads);
|
||||
|
||||
// Handle any remaining signals
|
||||
pcntl_signal_dispatch();
|
||||
|
||||
$this->RemoveLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes specified program in the current process space
|
||||
*
|
||||
* @param string $process Path to the binary process to execute
|
||||
* @param array $args Array of argument strings to pass to the program
|
||||
*/
|
||||
public function Exec($process, $args = null) {
|
||||
if ($args == null) {
|
||||
pcntl_exec($process);
|
||||
} else {
|
||||
pcntl_exec($process, $args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PID of the current process
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function GetPID() {
|
||||
return posix_getpid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PID of the process that spawned this one
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function GetControlPID() {
|
||||
return posix_getppid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of a running thread by name or PID
|
||||
*
|
||||
* @param string|integer $name The name or PID of the process for which you want status information
|
||||
* @return array|boolean
|
||||
*/
|
||||
public function GetThreadStatus($name = false) {
|
||||
if ($name === false) return false;
|
||||
if (isset($this->myThreads[$name])) {
|
||||
return $this->myThreads[$name];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the control process is daemonized
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function IsDaemon() {
|
||||
return $this->parentSID !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message
|
||||
*
|
||||
* @param string $msg The message to log
|
||||
* @param boolean $internal Whether this is an internal debug logging message
|
||||
*/
|
||||
public function Log($msg, $internal = false) {
|
||||
if ($this->logSocket !== false) {
|
||||
if (!$internal || $this->debugLogging) {
|
||||
if ($this->timeStampLogs) {
|
||||
fwrite($this->logSocket, sprintf("[%s][%-22s] %s\n", date("Y-m-d H:i:s"), $this->WhoAmI(), $msg));
|
||||
} else {
|
||||
fwrite($this->logSocket, sprintf("[%-22s] %s\n", $this->WhoAmI(), $msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts the control process
|
||||
*/
|
||||
public function Restart() {
|
||||
// Build Path of Script
|
||||
if (isset($_SERVER['_'])) {
|
||||
$cmd = $_SERVER['_'];
|
||||
$this->Log("Attempting to restart using {$cmd}",true);
|
||||
} else {
|
||||
$this->Log("Can not restart - Shutting down", true);
|
||||
return $this->Shutdown();
|
||||
}
|
||||
|
||||
// Wait for threads to complete
|
||||
while ($this->ThreadCount()) {
|
||||
$this->CheckThreads();
|
||||
$this->Tick();
|
||||
}
|
||||
|
||||
// Remove the first arg if this is a stand-alone
|
||||
if ($cmd == $_SERVER['argv'][0]) unset($_SERVER['argv'][0]);
|
||||
|
||||
// Execute Restart
|
||||
$this->Exec($cmd, $_SERVER['argv']);
|
||||
$this->Shutdown(true);
|
||||
return self::CALLBACK_IGNORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback function for the signal dispatcher or for special signals used by PowerProcess
|
||||
*
|
||||
* Special signals are:
|
||||
* - 'shutdown' : Triggered on completion of the Shutdown() method
|
||||
* - 'threadotl' : Triggered on killing a thread due to exceeding time limit
|
||||
*
|
||||
* @param int|string $signal The signal to register a callback for
|
||||
* @param callback $callback The callback function
|
||||
*/
|
||||
public function RegisterCallback($signal, $callback = false) {
|
||||
if ($callback !== false) $this->callbacks[$signal][] = $callback;
|
||||
|
||||
// Register with PCNTL
|
||||
if (is_int($signal)) {
|
||||
$this->Log("Registering signal {$signal} with dispatcher",true);
|
||||
pcntl_signal($signal, array(&$this, 'SignalDispatch'));
|
||||
|
||||
// Unblock the Signal
|
||||
pcntl_sigprocmask(SIG_UNBLOCK,array($signal));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we should be running the control code or the thread code
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function RunControlCode() {
|
||||
$this->Tick();
|
||||
if (!$this->complete) {
|
||||
return $this->ControlCheck();
|
||||
} else {
|
||||
$this->SignalDispatch('shutdown');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we should be running the child code
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function RunThreadCode() {
|
||||
return !$this->ControlCheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a signal to a process
|
||||
*
|
||||
* @param integer $pid
|
||||
* @param integer $signal
|
||||
*/
|
||||
public function SendSignal($pid = 0, $signal = 0) {
|
||||
if ($signal > 0 && $pid > 0) {
|
||||
return posix_kill($pid, $signal) && pcntl_signal_dispatch();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max number of threads that can be running concurrently
|
||||
*
|
||||
* @param integer $maxThreads The max number of threads to run concurrently
|
||||
*/
|
||||
public function SetMaxThreads($maxThreads = 10) {
|
||||
$this->maxThreads = $maxThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max number of seconds a thread can run before being terminated
|
||||
*
|
||||
* @param integer $threadTimeLimit The max number of seconds a thread can run
|
||||
*/
|
||||
public function SetThreadTimeLimit($threadTimeLimit = 300) {
|
||||
$this->threadTimeLimit = $threadTimeLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the shutdown procedure for PowerProcess
|
||||
*
|
||||
* @param boolean $exit When set to true, Shutdown causes the script to exit
|
||||
*/
|
||||
public function Shutdown($exit = false) {
|
||||
$this->Log("Initiating shutdown",true);
|
||||
|
||||
while ($this->ThreadCount()) {
|
||||
$this->CheckThreads();
|
||||
$this->Tick();
|
||||
}
|
||||
|
||||
$this->complete = true;
|
||||
|
||||
// Send custom shutdown signal
|
||||
$this->SignalDispatch('shutdown');
|
||||
|
||||
$this->Log("Shutdown Complete");
|
||||
if ($exit) exit;
|
||||
|
||||
return self::CALLBACK_IGNORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a new process can be spawned
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function SpawnReady() {
|
||||
$this->Tick();
|
||||
return ($this->ThreadCount() < $this->maxThreads);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn a new thread
|
||||
*
|
||||
* @param string $name The name of the thread to be spawned
|
||||
* @param boolean $returnPid Whether to return the pid instead of boolean
|
||||
*
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function SpawnThread($name = false, $returnPid = false) {
|
||||
// Check to make sure we can spawn another thread
|
||||
if (!$this->SpawnReady()) {
|
||||
$this->Log("The maximum number of threads are already running",true);
|
||||
$this->Tick();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($name !== false) {
|
||||
// Check to make sure there is not already a named thread with this name
|
||||
if ($this->GetThreadStatus($name) !== false) {
|
||||
$this->Log("There is already a thread named '{$name}' running",true);
|
||||
$this->Tick();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$pid = pcntl_fork();
|
||||
|
||||
if ($pid) {
|
||||
// We are the control thread so log the child in a queue
|
||||
$index = ($name === false) ? $pid : $name;
|
||||
$name = ($name === false) ? "THREAD:{$pid}" : $name;
|
||||
$this->myThreads[$index] = array(
|
||||
'pid' => $pid,
|
||||
'time' => time(),
|
||||
'name' => $name
|
||||
);
|
||||
$this->Log("Spawned thread: {$name}",true);
|
||||
$this->Tick();
|
||||
return ($returnPid) ? $pid : true;
|
||||
} else {
|
||||
// We are the child thread so change the current thread var
|
||||
$this->currentThread = ($name === false) ? "THREAD:".$this->GetPID() : $name;
|
||||
return ($returnPid) ? $pid : true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of running threads
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function ThreadCount() {
|
||||
return count($this->myThreads);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process signals to be dispatched and sleep for a number of microseconds
|
||||
*/
|
||||
public function Tick() {
|
||||
// Dispatch Pending Signals
|
||||
pcntl_signal_dispatch();
|
||||
|
||||
// Check Running Threads
|
||||
if ($this->parentPID == $this->GetPID()) $this->CheckThreads();
|
||||
|
||||
// Tick
|
||||
usleep($this->tickCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the current thread
|
||||
*
|
||||
* @return string The name of the current thread
|
||||
*/
|
||||
public function WhoAmI() {
|
||||
return $this->currentThread;
|
||||
}
|
||||
|
||||
// All Private Functions Below Here
|
||||
/**
|
||||
* Checks all running threads to make sure they are still running and their time limit has not been exceeded
|
||||
*
|
||||
* If a thread has exceeded it's time limit, this method will kill that process
|
||||
* and dispatch the special signal 'threadotl'
|
||||
*/
|
||||
private function CheckThreads() {
|
||||
foreach ($this->myThreads as $i => $thread) {
|
||||
// Check to make sure the process is still running
|
||||
if ($this->PIDDead($thread['pid']) != 0) {
|
||||
// Thread is Dead
|
||||
unset($this->myThreads[$i]);
|
||||
} elseif ($this->threadTimeLimit > 0) {
|
||||
if (time() - $thread['time'] > $this->threadTimeLimit) {
|
||||
$this->KillThread($thread['pid']);
|
||||
$this->Log("Thread {$thread['name']} has exceeded the thread time limit",true);
|
||||
$this->SignalDispatch('threadotl');
|
||||
unset($this->myThreads[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current process is the control process
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function ControlCheck() {
|
||||
return $this->parentPID == $this->GetPID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to daemonize the current process
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
private function Daemonize() {
|
||||
$this->Log("Attempting to Daemonize",true);
|
||||
|
||||
// First need to fork
|
||||
$pid = pcntl_fork();
|
||||
|
||||
// Tick to catch signals
|
||||
$this->Tick();
|
||||
|
||||
if ($pid < 0) exit; // Error
|
||||
if ($pid) exit; // Parent
|
||||
|
||||
$this->parentSID = posix_setsid();
|
||||
|
||||
// Need to reset the parent PID
|
||||
$this->parentPID = $this->GetPID();
|
||||
$this->Log("Parent PID {$this->parentPID}",true);
|
||||
$this->Log("Parent SID {$this->parentSID}",true);
|
||||
|
||||
return ($this->parentSID > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the logging stream if enabled
|
||||
*
|
||||
* @param string|boolean $logTo The path or stream to log to or false to disable
|
||||
*/
|
||||
private function InitializeLogger($logTo, $debugLogging) {
|
||||
if ($logTo !== false) {
|
||||
$this->logSocket = @fopen($logTo, 'w');
|
||||
$this->debugLogging = $debugLogging;
|
||||
} else {
|
||||
$this->logSocket = false;
|
||||
$this->debugLogging = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the default signal handlers
|
||||
*/
|
||||
private function InstallSignalHandler() {
|
||||
// Register the callback for thread completion
|
||||
$this->RegisterCallback(SIGCHLD, array($this,'CheckThreads'));
|
||||
$this->Log("SIGCHLD callback registered",true);
|
||||
|
||||
// Register the callback for restart requests
|
||||
$this->RegisterCallback(SIGHUP, array($this, 'Restart'));
|
||||
$this->Log("SIGHUP callback registered",true);
|
||||
|
||||
// Register the callback for shutdown requests
|
||||
$this->RegisterCallback(SIGTERM, array($this, 'Shutdown'));
|
||||
$this->Log("SIGTERM callback registered",true);
|
||||
|
||||
// Install the signal handler
|
||||
foreach ($this->signalArray as $signal) $this->RegisterCallback($signal);
|
||||
$this->Log("Signal Dispatcher installed",true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill a thread by PID
|
||||
*
|
||||
* @param integer $pid The PID of the thread to kill
|
||||
*/
|
||||
private function KillThread($pid = 0) {
|
||||
$this->SendSignal($pid, SIGTERM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a child pid has exited
|
||||
*
|
||||
* Returns the PID of child which exited or 0
|
||||
*
|
||||
* @param integer $pid The PID to check
|
||||
* @return integer
|
||||
*/
|
||||
private function PIDDead($pid = 0) {
|
||||
if ($pid > 0) {
|
||||
return pcntl_waitpid($pid, $status, WUNTRACED OR WNOHANG);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the logging stream
|
||||
*/
|
||||
private function RemoveLogger() {
|
||||
if ($this->logSocket) {
|
||||
@fclose($this->logSocket);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles dispatching of signals to user-defined callbacks
|
||||
*
|
||||
* @param integer|string $signal
|
||||
*/
|
||||
public function SignalDispatch($signal) {
|
||||
// Log Dispatch
|
||||
$signalName = self::SignalName($signal);
|
||||
$this->Log("Received signal: {$signalName}",true);
|
||||
|
||||
// Check the callback array for this signal number
|
||||
if (isset($this->callbacks[$signal])) {
|
||||
// Execute the callback
|
||||
$callStack = $this->callbacks[$signal];
|
||||
|
||||
// Run last added callbacks first (FILO)
|
||||
$i = count($callStack);
|
||||
while ($callback = array_pop($callStack)) {
|
||||
$this->Log("Running Callback[{$i}] for signal: {$signalName}", true);
|
||||
--$i;
|
||||
$status = call_user_func($callback);
|
||||
|
||||
// Check if we should continue
|
||||
if ($status == self::CALLBACK_STOP_PROPOGATION) {
|
||||
// Break out of loop
|
||||
$this->Log("Callback[{$i}] for signal '{$signalName}' has stopped propogation of further callbacks", true);
|
||||
break;
|
||||
}
|
||||
|
||||
// OK, now switch on the status
|
||||
switch ($status) {
|
||||
case self::CALLBACK_RESTART:
|
||||
$this->Restart();
|
||||
break;
|
||||
case self::CALLBACK_SHUTDOWN:
|
||||
$this->Shutdown();
|
||||
break;
|
||||
case self::CALLBACK_IGNORE:
|
||||
case self::CALLBACK_CONTINUE:
|
||||
// Do nothing :: Continue propogation
|
||||
break;
|
||||
default:
|
||||
$this->Log("Callback[{$i}] for signal '{$signalName}' did not return a valid status");
|
||||
}
|
||||
|
||||
// Continue the loop
|
||||
}
|
||||
} else {
|
||||
// No callback registered
|
||||
$this->Log("There is no callback registered for signal {$signalName}",true);
|
||||
}
|
||||
|
||||
// Handle SIGTERM for threads
|
||||
if ($signal == 15) exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user