--- /dev/null
+#!/usr/bin/env php
+<?php
+define('VERSION', '1.0a');
+function get_dir_tree($root)
+{
+ global $STH, $counter_dirs, $counter_files, $fh;
+ if (is_dir($root) === FALSE)
+ {
+ return FALSE;
+ }
+ $list = scandir($root);
+ foreach ($list as $item)
+ {
+ if ($item == '.' || $item == '..')
+ {
+ continue;
+ }
+ $path = $root.DIRECTORY_SEPARATOR.$item;
+ if (is_dir($path))
+ {
+ $counter_dirs ++;
+ get_dir_tree($path);
+ }
+ else
+ {
+ $counter_files ++;
+ $FIELD[0] = $path;
+ $FIELD[1] = filesize($path);
+ $STH->execute($FIELD);
+ }
+ }
+echo "reading directory tree... $counter_dirs dirs $counter_files files \r";
+}
+function help_topic($short = FALSE)
+{
+ global $argv;
+ echo 'File deduplicator ver. ', VERSION, PHP_EOL;
+ echo 'Usage: ', $argv[0],' [-options] directory',PHP_EOL;
+ echo 'Searches for identical files and allows you to remove duplicates', PHP_EOL ,'by deleting them or creating links instead.', PHP_EOL;
+ if ($short == TRUE)
+ {
+ echo 'Use -h for help.', PHP_EOL, PHP_EOL;
+ return;
+ }
+ $opts[] = '-n';
+ $descr[] = 'Action as do nothing (default).';
+ $opts[] = '-r';
+ $descr[] = 'Report to file in csv format, if file not set send to STDIN.';
+ $opts[] = '-d';
+ $descr[] = 'Delete duplicates. Conflict with -n -l -s.';
+ $opts[] = '-l';
+ $descr[] = 'Delete duplicates and create hardlinks. Duplicates must have same uid, gid and permissions, otherwise you should use -i. Conflict with -n -d -s.';
+ $opts[] = '-s';
+ $descr[] = 'Delete duplicates and create symlinks. Duplicates must have same uid, gid and permissions, otherwise you should use -i. Conflict with -n -d -l.';
+ $opts[] = '-i';
+ $descr[] = 'Ignore different uid, gid and permissions between duplicates. It will be used from the first file.';
+ $opts[] = '-h';
+ $descr[] = 'Show this help topic.';
+ $opts[] = '-v';
+ $descr[] = 'Be verbose while processing.';
+ $opts[] = '-V';
+ $descr[] = 'Show version.';
+
+
+ echo 'options:', PHP_EOL;
+ foreach ($opts as $key => $val)
+ {
+ preg_match_all('/(.{0,60})(\ |$)/', $descr[$key],$parts);
+ foreach ($parts[1] as $part)
+ {
+ if ($part == '')
+ {
+ continue;
+ }
+ echo "\t", $val, "\t", $part, PHP_EOL;
+ $val = '';
+ }
+ }
+ echo PHP_EOL;
+}
+
+function reporter()
+{
+ global $opts;
+ if (!isset($opts['r']))
+ {
+ return;
+ }
+
+}
+$counter_dirs = 0;
+$counter_files = 0;
+$tmpfile = '/tmp/dedupler.tmp';
+$optind = null;
+$opts = getopt('nrdlsihvV');
+var_dump($opts,$argv);
+if($argc > 1)
+{
+ $dir = array_pop ($argv);
+}
+
+if ($argc == 1)
+{
+ help_topic(TRUE);
+ exit();
+}
+
+
+if (isset($opts['V']))
+{
+ echo VERSION,PHP_EOL;
+ exit();
+}
+
+if (isset($opts['h']))
+{
+ help_topic();
+ exit();
+}
+
+
+$colopts['n'] = FALSE;
+$colopts['d'] = FALSE;
+$colopts['l'] = FALSE;
+$colopts['s'] = FALSE;
+
+if (count(array_diff_key ($colopts,$opts)) < 3 )
+{
+ echo 'You can not use options -n, -d, -l, -s together in any combinations, because it conflict between themselves.', PHP_EOL;
+ exit(2);
+
+}
+$_actmode = 0;
+
+if (isset($opts['d']))
+{
+ $_actmode = 1;
+}
+if (isset($opts['l']))
+{
+ $_actmode = 2;
+}
+if (isset($opts['s']))
+{
+ $_actmode = 3;
+}
+
+
+
+
+if (!is_dir($dir))
+{
+ echo $dir,' is not a directory!',PHP_EOL;
+ exit(1);
+}
+if (substr($dir, -1) == DIRECTORY_SEPARATOR)
+{
+ $dir = substr($dir,0,-1);
+}
+$_FSTREE = [];
+if (file_exists($tmpfile))
+{
+ unlink($tmpfile);
+}
+$DBH = new PDO("sqlite:".$tmpfile);
+$DBH->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
+$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
+$DBH->exec ('CREATE TABLE fstree
+ (
+ id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ path text,
+ size INTEGER
+ )
+ ');
+
+$STH = $DBH->prepare("INSERT INTO fstree (path, size) values (?, ?)");
+$DBH->beginTransaction();
+$tree = get_dir_tree($dir);
+$DBH->commit();
+$STH = $DBH->prepare("select path from fstree where size = ?");
+
+echo "\n";
+$stmt_sizes = $DBH->query('select size from fstree GROUP BY `size` HAVING count(*)>1');
+while ($size = $stmt_sizes->fetch())
+{
+ $size = implode($size);
+ echo PHP_EOL, "now analyzing group of files having size $size bytes:", PHP_EOL;
+ $STH->execute(array($size));
+ while ($path = $STH->fetch())
+ {
+ $path = implode($path);
+ if (!is_link($path))
+ {
+ $stat = stat($path);
+ if (!isset($first_flag))
+ {
+ $first_path = $path;
+ unset($first_md5);
+ $first_inodev = $stat['dev']."+".$stat['ino'];
+ $first_uidgid = $stat['uid']."+".$stat['gid'];
+ $first_flag = TRUE;
+ echo $path, ' is first file',PHP_EOL;
+ }
+ else
+ {
+ $cmp_inodev = $stat['dev']."+".$stat['ino'];
+ $cmp_uidgid = $stat['uid']."+".$stat['gid'];
+ if ($first_inodev == $cmp_inodev)
+ {
+ echo $path, ' is hardlink',PHP_EOL;
+ continue;
+ }
+ if (!isset($first_md5))
+ {
+ echo 'calculate md5 for first file',PHP_EOL;
+ $first_md5 = md5_file($first_path);
+ }
+ echo 'calculate md5 for next file',PHP_EOL;
+ $cmp_md5 = md5_file($path);
+ if ($first_md5 == $cmp_md5)
+ {
+ echo $path, ' is copy',PHP_EOL;
+ if ($mode == 0)
+ {
+
+ }
+ }
+ else
+ {
+ echo $path, ' is unique',PHP_EOL;
+
+ }
+ }
+
+// var_dump($path);
+// var_dump(stat($path));
+
+ }
+ }
+ unset($first_flag);
+
+}
+
--- /dev/null
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
--- /dev/null
+<?php\r
+\r
+/* \r
+ * To change this license header, choose License Headers in Project Properties.\r
+ * To change this template file, choose Tools | Templates\r
+ * and open the template in the editor.\r
+ */\r
+\r
+class Daemon\r
+{\r
+ private $unique_flag = FALSE;\r
+ private $pidfilename;\r
+\r
+ public function __construct()\r
+ {\r
+ }\r
+ \r
+ public function __destruct()\r
+ {\r
+ if ($this->unique_flag)\r
+ {\r
+ !unlink($this->pidfilename);\r
+ }\r
+ \r
+ }\r
+\r
+ public function daemonize()\r
+ {\r
+ $pid = pcntl_fork();\r
+ if ($pid == -1)\r
+ {\r
+ return FALSE;\r
+ }\r
+ elseif ($pid > 0)\r
+ {\r
+ exit;\r
+ }\r
+ umask(0);\r
+ chdir('/');\r
+ if (posix_setsid() == -1)\r
+ {\r
+ return FALSE;\r
+ }\r
+ \r
+ fclose(STDIN);\r
+ fclose(STDOUT);\r
+ fclose(STDERR);\r
+ $GLOBALS['STDIN'] = fopen('/dev/null', 'r');\r
+ $GLOBALS['STDOUT']= fopen('/dev/null', 'w');\r
+ $GLOBALS['STDERR'] = fopen('/dev/null', 'w');\r
+ \r
+ return TRUE;\r
+ }\r
+ \r
+ public function is_unique($pidfilename)\r
+ {\r
+ $mypid = posix_getpid();\r
+ if (is_readable($pidfilename))\r
+ {\r
+ $pid = (int) rtrim(file_get_contents($pidfilename));\r
+ if ($pid == $mypid)\r
+ {\r
+ return TRUE;\r
+ }\r
+ if ($pid > 0 && posix_kill($pid, 0))\r
+ {\r
+ return FALSE;\r
+ }\r
+ }\r
+ return TRUE;\r
+\r
+ }\r
+ \r
+ public function no_unique($pidfilename)\r
+ {\r
+ if (!unlink($pidfilename))\r
+ {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ public function do_unique($pidfilename)\r
+ {\r
+ if (!$this->is_unique($pidfilename))\r
+ {\r
+ return FALSE;\r
+ }\r
+\r
+ $mypid = posix_getpid();\r
+ if (!file_put_contents($pidfilename, $mypid.PHP_EOL))\r
+ {\r
+ return FALSE;\r
+ }\r
+ return TRUE;\r
+ }\r
+} \r
+\r
+class Children \r
+{\r
+ protected $PIPE;\r
+ protected $ROLE;\r
+ protected static $CHILDREN;\r
+ protected $CHILD;\r
+ protected $PARENT;\r
+\r
+\r
+ public function __construct()\r
+ {\r
+ self::set_sig_handlers();\r
+ $this->create_child();\r
+ switch ($this->ROLE)\r
+ {\r
+ case -1:\r
+ return FALSE;\r
+ case 0:\r
+ return TRUE;\r
+ case 1:\r
+ $this->ChildBody();\r
+ exit();\r
+ }\r
+ \r
+ }\r
+ \r
+ public function __destruct()\r
+ {\r
+ if ($this->ROLE == 0)\r
+ {\r
+ posix_kill($this->CHILD, SIGTERM);\r
+ }\r
+ }\r
+\r
+ protected function ChildBody()\r
+ {\r
+ \r
+ }\r
+\r
+ static function set_sig_handlers()\r
+ {\r
+ pcntl_signal(SIGCHLD, [__CLASS__, 'handler_sigchld']);\r
+ }\r
+ \r
+ public function SendUnixSignal($sig)\r
+ {\r
+ return posix_kill($this->CHILD, $sig);\r
+ }\r
+ \r
+ public function IsAlive()\r
+ {\r
+ if ($this->ROLE == 1)\r
+ {\r
+ return TRUE;\r
+ }\r
+ if (isset(self::$CHILDREN[$this->CHILD]))\r
+ {\r
+ return TRUE;\r
+ }\r
+ else\r
+ {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ public static function handler_sigchld()\r
+ { \r
+ $pid = pcntl_waitpid(0, $status, WNOHANG);\r
+ if($pid > 0)\r
+ {\r
+ unset(self::$CHILDREN[$pid]);\r
+ }\r
+ }\r
+\r
+ protected function create_child()\r
+ {\r
+ $sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);\r
+ stream_set_blocking ($sockets[0] , 0);\r
+ stream_set_blocking ($sockets[1] , 0);\r
+ $pid = pcntl_fork();\r
+ if ($pid == -1)\r
+ {\r
+ $this->ROLE = -1;\r
+ return FALSE;\r
+ }\r
+ elseif ($pid > 0)\r
+ {\r
+ // код для родителя\r
+ $this->CHILD = $pid;\r
+ self::$CHILDREN[$pid] = $pid;\r
+ $this->PARENT = FALSE;\r
+ $this->ROLE = 0;\r
+ $this->PIPE = &$sockets[0];\r
+ unset($sockets);\r
+ }\r
+ elseif ($pid == 0)\r
+ {\r
+ // код для ребёнка\r
+ $this->CHILD = FALSE;\r
+ $this->PARENT = posix_getppid();\r
+ $this->ROLE = 1;\r
+ $this->PIPE = &$sockets[1];\r
+ unset($sockets);\r
+ }\r
+ return TRUE;\r
+ }\r
+ \r
+ public function SendEvent($eventname, $data)\r
+ {\r
+ if (!is_resource($this->PIPE))\r
+ {\r
+ return FALSE;\r
+ }\r
+ $eventpack = base64_encode($eventname).chr(255).chr(0).chr(255).base64_encode(serialize($data)).chr(0).chr(15).chr(240).chr(255);\r
+ \r
+ $res = @stream_socket_sendto($this->PIPE, $eventpack); \r
+ if ($res == -1)\r
+ {\r
+ return TRUE;\r
+ }\r
+ else\r
+ {\r
+ return FALSE;\r
+ }\r
+ }\r
+ \r
+ public function WaitEvent($wait = TRUE)\r
+ {\r
+ if (!is_resource($this->PIPE))\r
+ {\r
+ return FALSE;\r
+ }\r
+ \r
+ $r = [$this->PIPE];\r
+ if ($wait)\r
+ {\r
+ while (stream_select($r, $w, $x, 0) == 0)\r
+ {\r
+ usleep(10000);\r
+ $r = [$this->PIPE];\r
+ }\r
+ }\r
+ elseif (stream_select($r, $w, $x, 0) == 0)\r
+ {\r
+ return NULL;\r
+ }\r
+ $ipc_msg = '';\r
+ while (substr($ipc_msg , -4) != chr(0).chr(15).chr(240).chr(255))\r
+ {\r
+ $rcv_buf = stream_socket_recvfrom($this->PIPE, 1500);\r
+ var_dump($rcv_buf);\r
+ if ($rcv_buf == '')\r
+ {\r
+ return NULL;\r
+ }\r
+ $ipc_msg .= $rcv_buf;\r
+ }\r
+ $ipc_msg = substr($ipc_msg,0,-4);\r
+ $ipc_msg = explode(chr(255).chr(0).chr(255), $ipc_msg);\r
+ $retval['eventname'] = base64_decode($ipc_msg[0]);\r
+ $retval['data'] = unserialize(base64_decode($ipc_msg[1]));\r
+ \r
+ return $retval;\r
+ \r
+ }\r
+}\r
--- /dev/null
+<?php
+register_tick_function(array('Timer','poll'));
+
+class Timer
+{
+ private $id; //метка
+ private $time; //таймер
+ private $IsStop; //флаг остановленности true/false
+ private $UserCall; //задача
+ private $UserCallArg; //параметр к задаче
+ private $StartTime; //момент запуска таймера
+ private $ltime; //оставшееся время
+ private $type; //тип true регенерируемый, false не регенерируемый
+ private static $timers = [];
+
+ public function __construct($time, $callable, $type = false)
+ {
+ $this->id = uniqid();
+ $this->time = $time;
+ $this->UserCall = $callable;
+ $this->type = $type;
+ $this->start();
+ self::$timers[$this->id] = &$this;
+ }
+
+ public function __destruct()
+ {
+ unset(self::$timers[$this->id]);
+ }
+
+ private function getId()
+ {
+ return $this->id;
+ }
+
+ public function getTimeLeft()
+ {
+ return $this->ltime;
+ }
+
+ public function setTimeLeft($time)
+ {
+ $this->time = $time;
+ }
+
+
+ public function reset()
+ {
+ $this->StartTime = microtime(true);
+ $this->IsStop = FALSE;
+ }
+
+ public function start()
+ {
+ $this->reset();
+ $this->IsStop = FALSE;
+ }
+
+ public function stop()
+ {
+ $this->IsStop = TRUE;
+ }
+
+ public static function poll()
+ {
+ foreach (self::$timers as $timer)
+ {
+ if ($timer->IsStop == FALSE)
+ {
+ $timer->update();
+ }
+ }
+ }
+
+ private function update()
+ {
+ $this->ltime = $this->StartTime - microtime(true) + $this->time;
+ if ($this->ltime <= 0)
+ {
+ $this->ltime = 0;
+ $this->task();
+ if ($this->type == FALSE)
+ {
+ $this->stop();
+ }
+ else
+ {
+ $this->reset();
+ }
+ }
+ }
+
+ private function task()
+ {
+ call_user_func_array ($this->UserCall, $this->UserCallArg = []);
+ }
+}
--- /dev/null
+[common]
+pidfile = /var/run/dvbmaster.pid
+dvblast_path = /usr/local/bin/dvblast
+dvblastctl_path = /usr/local/bin/dvblastctl
+es_timeout = 3000
+epg_passthrough = yes
+;provider_name = pizda
+;network_id = azaz
+;network_name = aaaa
+ttl = 64
+use_rtp = yes
+pidmap = 100,200,300,400
+run_user = nobody
+run_group = video
+
+
+[adapters]
+podryad1 = /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8.3
+podryad2 = /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8.2
+adap1 = /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8.1/1-8.1.2
+adap2 = /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8.1/1-8.1.4
+
+[enable]
+;podryad1 = mux-1
+podryad2 = mux-1
+;adap1 = off
+adap2 = mux-1
+
+[mux-1]
+delivery_system = DVBC_ANNEX_A
+frequency = 778000000
+symbol_rate = 6875000
+out[] = 239.1.2.51:2020 1 2701
+out[] = 239.1.2.52:2020 1 2702
+out[] = 239.1.2.53:2020 1 2703
+out[] = 239.1.2.54:2020 1 2704
+out[] = 239.1.2.55:2020 1 2705
+out[] = 239.1.2.56:2020 1 2706
+out[] = 239.1.2.57:2020 1 2707
+out[] = 239.1.2.58:2020 1 2708
+out[] = 239.1.2.59:2020 1 2709
+out[] = 239.1.2.60:2020 1 2710
+out[] = 239.1.2.61:2020 1 2711
+out[] = 239.1.2.62:2020 1 2712
+out[] = 239.1.2.63:2020 1 2713
+
+
+[mux-2]
+delivery_system = DVBT2
+frequency = 610000000
+symbol_rate = 6875000
+plp_id = 0
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env php
+<?php
+// library
+require_once 'lib/sts.php';
+require_once 'lib/mplib.php';
+require_once 'lib/timer.php';
+
+// main code class
+class dvbmaster
+{
+ protected $IniConfig;
+ protected $IniFile = 'dvbmaster.ini';
+ protected $WorkCommonParams = [];
+ protected $WorkAdaptersCfg = [];
+ protected $Multiplexes = [];
+ protected $dvblastctl;
+ protected $RUN = FALSE;
+ protected $XmlStatsData = [];
+ protected $RunAsDaemon = FALSE;
+
+ public function __construct()
+ {
+ $this->CliParseOptions();
+ STS::stdout_prefix('dvbmaster: ');
+
+ declare(ticks = 1);
+ cli_set_process_title('dvbmaster: master process');
+ if (!$this->ReadConfig())
+ {
+ echo "ERROR: Reading config file error!\n";
+ exit();
+ }
+ if (!isset($this->WorkCommonParams['pidfile']))
+ {
+ echo "ERROR: Pidfile not defined in config file!\n";
+ exit();
+ }
+ $_PIDFILENAME = &$this->WorkCommonParams['pidfile'];
+
+ if ($this->RunAsDaemon)
+ {
+ Daemon::daemonize();
+ }
+ if (!Daemon::is_unique($_PIDFILENAME))
+ {
+ echo "ERROR: This program already runed!\n";
+ exit();
+ }
+ if (!Daemon::do_unique($_PIDFILENAME))
+ {
+ echo "ERROR: Can't make this program instance is unique!\n";
+ exit();
+
+ }
+ $this->SetUidGid();
+
+ pcntl_signal(SIGHUP, [$this, 'handler_sighup']);
+ pcntl_signal(SIGTERM, [$this, 'handler_sigintterm']);
+ pcntl_signal(SIGINT, [$this, 'handler_sigintterm']);
+
+
+ $this->dvblastctl = new dvblastctl();
+ $this->dvblastctl->SendEvent('updateconfig', $this->GetConfigForDvblastctl());
+ $this->RUN = TRUE;
+ while ($this->RUN)
+ {
+ $this->loop();
+ sleep(1);
+ }
+ $this->WaitForShutdown();
+
+ }
+
+ public function __destruct()
+ {
+ STS::stdout_unprefix();
+
+ }
+ public function handler_sighup()
+ {
+ echo "Got SIGHUP, rereading configuration file...\n";
+ if (!$this->ReadConfig())
+ {
+ echo "Reading config error!\n";
+ }
+ else
+ {
+ $this->dvblastctl->SendEvent('updateconfig', $this->GetConfigForDvblastctl());
+
+ echo "Reread configuration file is finished!\n";
+ }
+ }
+
+ public function handler_sigintterm()
+ {
+ echo "Got SIGINT/SIGTERM, wait for the completion of worker processes...\n";
+ $this->RUN = FALSE;
+ $this->dvblastctl->SendEvent('shutdown', NULL);
+ }
+ protected function WaitForShutdown()
+ {
+ for ($c = 0; $c < 15; $c++)
+ {
+ sleep(1);
+ if (!$this->dvblastctl->IsAlive())
+ {
+ break;
+ }
+
+ }
+
+
+
+ }
+
+ public function loop()
+ {
+ echo 'LOOP'.PHP_EOL;
+ $this->EventReciever();
+ }
+
+ protected function EventReciever()
+ {
+ while (($DvblastctlMessage = $this->dvblastctl->WaitEvent(FALSE)) !== NULL && $DvblastctlMessage !== FALSE)
+ {
+ $this->EventProcessor('dvblastctl',$DvblastctlMessage);
+ }
+ }
+
+ protected function EventProcessor($module, $message)
+ {
+ if ($module == 'dvblastctl')
+ {
+ switch ($message['eventname'])
+ {
+ case 'updatestats':
+ $this->XmlStatsData = array_replace_recursive($this->XmlStatsData, $message['data']);
+ break;
+
+ }
+ }
+ }
+
+
+ // сканер физических устройств DVB
+ // вход: нет
+ // выход array устройства из udev
+ public function ScanDvbDevices()
+ {
+
+ echo 'scan devices...',PHP_EOL;
+ if (!file_exists('/dev/dvb/'))
+ {
+ exit("dvb frontend devices not found\n");
+ }
+ else
+ {
+ exec ('find /dev/dvb/ -name "frontend*"', $devnames);
+ if (count($devnames) == 0)
+ {
+ exit("dvb frontend devices not found\n");
+ }
+ }
+ $DVBDevs = [];
+ foreach ($devnames as $devname)
+ {
+ exec ('udevadm info '.$devname, $devinfo);
+ foreach ($devinfo as $str)
+ {
+ if(preg_match('/^.:\s(.+)=(.*)$/', $str, $d) > 0)
+ {
+ $DVBDevs[$devname][$d[1]] = $d[2];
+ }
+ if (isset($DVBDevs[$devname]['DEVPATH']))
+ {
+ $DVBDevs[$devname]['DEVPORT'] = preg_replace ('/\/dvb\/.*/', '', $DVBDevs[$devname]['DEVPATH']);
+ }
+
+ }
+ unset($devinfo,$d,$str);
+ }
+ return $DVBDevs;
+ }
+
+
+ public function GenerateDevConfig($DVBDevs)
+ {
+ $adaptername = 0;
+ $ini = [];
+ foreach ($DVBDevs as $name => $data)
+ {
+ $ini['adapters']['adaptername-'.$adaptername] = $data['DEVPORT'];
+
+ $adaptername++;
+ }
+ echo STS::generate_ini($ini);
+ }
+
+ public function HelpTopic()
+ {
+ echo "This is a help topic \n";
+ }
+
+ public function CliParseOptions()
+ {
+ $shortoptlist = 'hsdc:';
+ $longoptlist = ['help','scandev', 'daemon', 'config-file:'];
+ $opts = getopt($shortoptlist, $longoptlist, $optindex);
+ var_dump($opts, $optindex);
+
+ if (isset($opts['h']) || isset($opts['help']))
+ {
+ $this->HelpTopic();
+ exit();
+ }
+
+ if (isset($opts['s']) || isset($opts['scandev']))
+ {
+ $scandev = $this->ScanDvbDevices();
+ echo "\n;Insert this output to your configuration file (.ini) file.\n\n";
+ $this->GenerateDevConfig($scandev);
+ exit();
+ }
+
+ if (isset($opts['d']) || isset($opts['daemon']))
+ {
+ $this->RunAsDaemon = TRUE;
+ }
+
+ if (isset($opts['c']))
+ {
+ $IniFile = $opts['c'];
+ }
+
+ if (isset($opts['config-file']))
+ {
+ $IniFile = $opts['config-file'];
+ }
+ if (is_string($IniFile))
+ {
+ $this->IniFile = $IniFile;
+ }
+
+ }
+
+ public function ReadConfig()
+ {
+ $ini_file = &$this->IniFile;
+ if (!file_exists($ini_file))
+ {
+ echo "ERROR: Configuration file {$ini_file} not found. \n";
+ return FALSE;
+ }
+ $this->IniConfig = @parse_ini_file($ini_file, TRUE, INI_SCANNER_TYPED);
+ if ($this->IniConfig === FALSE)
+ {
+ echo "ERROR: Configuration file {$ini_file} contain syntax errors. \n";
+ return FALSE;
+ }
+ if (!isset($this->IniConfig['adapters']))
+ {
+ return FALSE;
+ }
+
+ $this->Multiplexes = [];
+ $this->WorkAdaptersCfg = [];
+ $this->WorkCommonParams = [];
+
+ $ScannedDevs = $this->ScanDvbDevices();
+ foreach ($this->IniConfig['adapters'] as $AdapterName => $AdapterPort)
+ {
+ foreach ($ScannedDevs as $Dev)
+ {
+ if ($AdapterPort == $Dev['DEVPORT'])
+ {
+ $this->WorkAdaptersCfg[$AdapterName]['DEVNAME'] = $Dev['DEVNAME'];
+ $this->WorkAdaptersCfg[$AdapterName]['ADAPTER_NUM'] = $Dev['DEVPORT'];
+ $this->WorkAdaptersCfg[$AdapterName]['ADAPTER_NUM'] = $Dev['DVB_ADAPTER_NUM'];
+ $this->WorkAdaptersCfg[$AdapterName]['FRONTEND_NUM'] = $Dev['DVB_DEVICE_NUM'];
+ if (isset($this->IniConfig['enable'][$AdapterName]))
+ {
+ $this->WorkAdaptersCfg[$AdapterName]['MUX'] = $this->IniConfig['enable'][$AdapterName];
+ }
+ else
+ {
+ $this->WorkAdaptersCfg[$AdapterName]['MUX'] = FALSE;
+ }
+ }
+ }
+ }
+ $ValidDelSys = ['DVBS', 'DVBS2', 'DVBC_ANNEX_A', 'DVBT','DVBT2', 'ATSC','ISDBT','DVBC_ANNEX_B'];
+ $ValidModulationC = ['qpsk','qam_16','qam_32','qam_64','qam_128','qam_256'];
+ $ValidModulationT = ['qam_16','qam_32','qam_64','qam_128','qam_256'];
+ $ValidModulationS = ['qpsk','psk_8','apsk_16','apsk_32'];
+ foreach ($this->IniConfig as $SectionName => $Section)
+ {
+ if (isset($Section['delivery_system']))
+ {
+ if(array_search($Section['delivery_system'], $ValidDelSys) !== FALSE)
+ {
+ $this->Multiplexes[$SectionName] = $Section;
+
+ }
+ if (isset($this->Multiplexes[$SectionName]['modulation']))
+ {
+ $modname = $this->Multiplexes[$SectionName]['modulation'];
+ if (($this->Multiplexes[$SectionName]['delivery_system'] == 'DVBS' || $this->Multiplexes[$SectionName]['delivery_system'] == 'DVBS2') && array_search($this->Multiplexes[$SectionName]['modulation'], $ValidModulationS) === FALSE)
+ {
+ $this->Multiplexes[$SectionName]['modulation'] = FALSE;
+ }
+ if (($this->Multiplexes[$SectionName]['delivery_system'] == 'DVBC_ANNEX_A' || $this->Multiplexes[$SectionName]['delivery_system'] == 'DVBC_ANNEX_B')&& array_search($this->Multiplexes[$SectionName]['modulation'], $ValidModulationC) === FALSE)
+ {
+ $this->Multiplexes[$SectionName]['modulation'] = FALSE;
+ }
+ if (($this->Multiplexes[$SectionName]['delivery_system'] == 'DVBT' || $this->Multiplexes[$SectionName]['delivery_system'] == 'DVBT2')&& array_search($this->Multiplexes[$SectionName]['modulation'], $ValidModulationT) === FALSE)
+ {
+ $this->Multiplexes[$SectionName]['modulation'] = FALSE;
+ }
+ if ($this->Multiplexes[$SectionName]['modulation'] === FALSE)
+ {
+ echo "WARNING: In MUX '{$SectionName}' modulation '{$modname}' can't be used with system {$this->Multiplexes[$SectionName]['delivery_system']}, using default.\n";
+ unset($this->Multiplexes[$SectionName]['modulation']);
+ }
+ }
+ }
+ }
+
+ foreach ($this->WorkAdaptersCfg as $AdapterName => $AdapterCfg)
+ {
+ if ($AdapterCfg['MUX'] === FALSE)
+ {
+ continue;
+ }
+ if (!isset($this->Multiplexes[$AdapterCfg['MUX']]))
+ {
+ echo "ERROR: Multiplex configuration with name {$AdapterCfg['MUX']} for adapter {$AdapterName} not found.\n";
+ return FALSE;
+ }
+ }
+
+ if (!isset($this->IniConfig['common']['dvblast_path']))
+ {
+ echo "ERROR: Common configuration 'dvblast_path' not found\n";
+ return FALSE;
+ }
+ if (!isset($this->IniConfig['common']['dvblastctl_path']))
+ {
+ echo "ERROR: Common configuration 'dvblastctl_path' not found\n";
+ return FALSE;
+ }
+ if (!file_exists($this->IniConfig['common']['dvblast_path']))
+ {
+ echo "ERROR: File in 'dvblast_path' not found\n";
+ return FALSE;
+ }
+ if (!file_exists($this->IniConfig['common']['dvblastctl_path']))
+ {
+ echo "ERROR: File in 'dvblastctl_path' not found\n";
+ return FALSE;
+ }
+
+ $this->WorkCommonParams = $this->IniConfig['common'];
+ return TRUE;
+
+ }
+
+ public function GetConfigForDvblastctl()
+ {
+ $config = [];
+ $config['Multiplexes'] = $this->Multiplexes;
+ $config['WorkAdaptersCfg'] = $this->WorkAdaptersCfg;
+ $config['WorkCommonParams'] = $this->WorkCommonParams;
+ return $config;
+ }
+
+ public function SetUidGid()
+ {
+ $FAILFLAG = FALSE;
+ $_PIDFILENAME = &$this->WorkCommonParams['pidfile'];
+ if (isset($this->WorkCommonParams['run_group']))
+ {
+ if(!chgrp($_PIDFILENAME, $this->WorkCommonParams['run_group']))
+ {
+ echo 'WARNING: Could not change group for file ',$_PIDFILENAME;
+ }
+ echo "Set process group (GID) to {$this->WorkCommonParams['run_group']} ...";
+ $userinfo = posix_getgrnam($this->WorkCommonParams['run_group']);
+ if ($userinfo === FALSE)
+ {
+ echo "FAIL\n";
+ $FAILFLAG = TRUE;
+
+ }
+ elseif (!posix_setgid($userinfo['gid']))
+ {
+ echo "FAIL\n";
+ $FAILFLAG = TRUE;
+ }
+ else
+ {
+ echo "DONE\n";
+ }
+ }
+ if (isset($this->WorkCommonParams['run_user']))
+ {
+ if(!chown($_PIDFILENAME, $this->WorkCommonParams['run_user']))
+ {
+ echo 'WARNING: Could not change user for file ',$_PIDFILENAME;
+ }
+ echo "Set process user (UID) to {$this->WorkCommonParams['run_user']} ...";
+ $userinfo = posix_getpwnam($this->WorkCommonParams['run_user']);
+ if ($userinfo === FALSE)
+ {
+ echo "FAIL\n";
+ $FAILFLAG = TRUE;
+
+ }
+ elseif (!posix_setuid($userinfo['uid']))
+ {
+ echo "FAIL\n";
+ $FAILFLAG = TRUE;
+ }
+ else
+ {
+ echo "DONE\n";
+ }
+ }
+
+ return !$FAILFLAG;
+ }
+
+}
+// класс контроллера процессов dvblast
+class dvblastctl extends Children
+{
+ protected $PIPES = [];
+ protected $dvbprc = [];
+ protected $runcmd = [];
+ protected $errbuffer = [];
+ protected $RestartFlag = [];
+ protected $config;
+ protected $wdlock = FALSE;
+ protected $usdlock = FALSE;
+ protected $timers = [];
+ protected $RUN = TRUE;
+ protected $StatMd5 = [];
+ protected $QueueUpdateStatData = [];
+
+
+ public function handler_sig($SIG)
+ {
+ $SIGMNEMO = STS::GetSignalMnemonicByNumber($SIG);
+ if ($SIGMNEMO === FALSE)
+ {
+ $SIGMNEMO = 'unknown';
+ }
+ $this->wdlock = TRUE;
+ echo "Got '{$SIGMNEMO}' ({$SIG}) signal! Shutting down all dvblast process...\n";
+ $this->timers['WatchdogDvblast']->stop();
+ $this->timers['UpdateMsgBuffer']->stop();
+ $this->timers['GetMsgBuffer']->stop();
+ foreach ($this->dvbprc as $procname => $procdesc)
+ {
+ $this->StopDvblast($procname);
+ }
+ echo "All processes was shuted down.\n";
+ $this->RUN = FALSE;
+
+ }
+
+ public function __destruct()
+ {
+ parent::__destruct();
+ if ($this->ROLE == MPRole_CHILD)
+ {
+ STS::stdout_unprefix();
+ }
+
+ }
+ protected function ChildBody($INPARAM)
+ {
+ //init
+ declare(ticks = 1);
+ cli_set_process_title('dvblastctl: controller process');
+ unset($GLOBALS['PRG']);
+ STS::stdout_unprefix();
+ STS::stdout_prefix('dvblastctl: ');
+ pcntl_signal(SIGTERM, [&$this, 'handler_sig']);
+ pcntl_signal(SIGINT, [&$this, 'handler_sig']);
+ pcntl_signal(SIGHUP,SIG_IGN);
+ echo "remove temporary files...";
+ @array_map('unlink', glob("/tmp/*.dvbmcfg"));
+ echo "DONE\n";
+ //get config after starts
+ echo "wait for config...";
+ $event = $this->WaitEvent(TRUE);
+ if ($event['eventname'] != 'updateconfig')
+ {
+ echo "FAIL\nERROR: fetched is not config.\n";
+ exit(1);
+ }
+ $this->config = $event['data'];
+ echo "DONE\n";
+
+ //start dvblast pool
+ $CmdParams = $this->PrepareDvblastParams($this->config);
+
+ foreach ($CmdParams as $NameInstance => $CmdParam)
+ {
+ $this->StartDvblast($NameInstance, $CmdParam);
+ }
+ $ListUpdateStatData = ['fe_status' => 60, 'mmi_status' => 100, 'get_pat' => 300, 'get_cat' => 300, 'get_nit' => 300, 'get_sdt' => 300, 'get_pids' => 10];
+ $this->timers['WatchdogDvblast'] = new Timer(30, [$this, 'WatchdogDvblast'], NULL, TRUE);
+ $this->timers['UpdateMsgBuffer'] = new Timer(10, [$this, 'UpdateMsgBuffer'], NULL, TRUE);
+ $this->timers['UpdateStatData'] = new Timer(10, [$this, 'UpdateStatData'], NULL, TRUE);
+ $this->timers['GetMsgBuffer'] = new Timer(3600, [$this, 'GetMsgBuffer'], NULL, TRUE);
+ foreach ($ListUpdateStatData as $infcmd => $time)
+ {
+ $this->timers['AddToQueueUpdateStatData'] = new Timer($time, [$this, 'AddToQueueUpdateStatData'], [$infcmd], TRUE);
+ $this->AddToQueueUpdateStatData($infcmd);
+ }
+ while ($this->RUN)
+ {
+ sleep(1);
+ $this->loop();
+ }
+ echo "Shuted down controller process\n";
+ STS::stdout_unprefix();
+ }
+
+ protected function EventProcessor()
+ {
+ while (($event = $this->WaitEvent(FALSE)) !== NULL && $event !== FALSE)
+ {
+ switch ($event['eventname'])
+ {
+ case 'updateconfig':
+ $this->ReuseConfig($event['data']);
+ break;
+ case 'shutdown':
+ $this->handler_sig(3);
+ break;
+ }
+ }
+ }
+
+ protected function ReuseConfig($NewConfig)
+ {
+ $result = array_udiff_assoc($NewConfig, $this->config, function ($a, $b) {return (int)!($a === $b);});
+ $stoplist = [];
+ $startlist = [];
+ if (isset($result['WorkCommonParams']))
+ {
+ $stoplist = array_keys($this->dvbprc);
+ foreach ($stoplist as $AdapterName)
+ {
+ $this->StopDvblast($AdapterName);
+ }
+ $this->config = $NewConfig;
+ $CmdParams = $this->PrepareDvblastParams($this->config);
+ foreach ($CmdParams as $NameInstance => $CmdParam)
+ {
+ $this->StartDvblast($NameInstance, $CmdParam);
+ }
+ return;
+ }
+ if (isset($result['WorkAdaptersCfg']))
+ {
+ $stoplist = array_keys(array_udiff_assoc($this->config['WorkAdaptersCfg'], $NewConfig['WorkAdaptersCfg'], function ($a, $b) {return (int)!($a === $b);}));
+ $startlist = array_keys(array_udiff_assoc($NewConfig['WorkAdaptersCfg'], $this->config['WorkAdaptersCfg'], function ($a, $b) {return (int)!($a === $b);}));
+ }
+ if (isset($result['Multiplexes']))
+ {
+ $omux = array_keys(array_udiff_assoc($this->config['Multiplexes'], $NewConfig['Multiplexes'], function ($a, $b) {return (int)!($a === $b);}));
+ $diffmux = array_keys(array_udiff_assoc($NewConfig['Multiplexes'], $this->config['Multiplexes'], function ($a, $b) {return (int)!($a === $b);}));
+ foreach ($this->config['WorkAdaptersCfg'] as $AdapterName => $AdapterConfig)
+ {
+ if (array_search($AdapterConfig['MUX'], $omux) !== FALSE)
+ {
+ $stoplist[] = $AdapterName;
+ }
+ }
+ foreach ($NewConfig['WorkAdaptersCfg'] as $AdapterName => $AdapterConfig)
+ {
+ if (array_search($AdapterConfig['MUX'], $diffmux) !== FALSE)
+ {
+ $startlist[] = $AdapterName;
+ }
+ }
+
+ }
+
+ $stoplist = array_unique($stoplist);
+ $startlist = array_unique($startlist);
+ foreach ($stoplist as $NameInstance)
+ {
+ if (isset($this->dvbprc[$NameInstance]))
+ {
+ $this->StopDvblast($NameInstance);
+ }
+ }
+
+ $this->config = $NewConfig;
+ $CmdParams = $this->PrepareDvblastParams($this->config);
+ foreach ($startlist as $NameInstance)
+ {
+ if (isset($CmdParams[$NameInstance]))
+ {
+ $this->StartDvblast($NameInstance, $CmdParams[$NameInstance]);
+ }
+ }
+
+ }
+
+ public function AddToQueueUpdateStatData($infcmd)
+ {
+ $this->QueueUpdateStatData[] = $infcmd;
+ }
+
+ public function UpdateStatData()
+ {
+ if ($this->wdlock || $this->usdlock)
+ {
+ return;
+ }
+ $this->usdlock = TRUE;
+ $AdapterNames = array_keys($this->dvbprc);
+ $this->QueueUpdateStatData = array_unique($this->QueueUpdateStatData);
+ while (count($this->QueueUpdateStatData) > 0)
+ {
+ $infcmd = array_pop($this->QueueUpdateStatData);
+ foreach ($AdapterNames as $AdapterName)
+ {
+ $cmd = $this->config['WorkCommonParams']['dvblastctl_path'].' -r /tmp/dvb-'.$AdapterName.'.sock -t 5 -x xml ';
+ $xml = shell_exec($cmd.$infcmd.' 2>&1');
+ if (!isset($this->StatMd5[$AdapterName][$infcmd]))
+ {
+ $this->StatMd5[$AdapterName][$infcmd] = NULL;
+ }
+ $XmlMd5 = md5($xml);
+ if ($this->StatMd5[$AdapterName][$infcmd] == $XmlMd5)
+ {
+ continue;
+ }
+ $this->StatMd5[$AdapterName][$infcmd] = $XmlMd5;
+ unset($SendData);
+ $SendData[$AdapterName][$infcmd] = $xml;
+ $this->SendEvent('updatestats', $SendData);
+ }
+ }
+ $this->usdlock = FALSE;
+ }
+
+ protected function StartDvblast($name,$runcmd)
+ {
+ $this->wdlock = TRUE;
+ $descriptorspec = [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']];
+ echo "starting process '{$name}'...";
+ $this->runcmd[$name] = $runcmd;
+ $this->errbuffer[$name] = [];
+ $this->dvbprc[$name] = proc_open('exec '.$runcmd, $descriptorspec, $this->PIPES[$name]);
+ foreach ($this->PIPES[$name] as $procpipe)
+ {
+ stream_set_blocking($procpipe,0);
+ }
+ $procstatus = proc_get_status($this->dvbprc[$name]);
+ if ($procstatus['running'])
+ {
+ echo "OK\n";
+ }
+ else
+ {
+ echo "FAIL\n";
+ $this->StopDvblast($name);
+ }
+ $this->wdlock = FALSE;
+ new Timer(3, [$this, 'WatchdogDvblast'], NULL, NULL);
+ }
+ protected function StopDvblast($name)
+ {
+ $this->wdlock = TRUE;
+ echo "shutdown process '{$name}'...";
+ proc_terminate ($this->dvbprc[$name]);
+ for ($i=0;$i<10;$i++)
+ {
+ $procstatus = proc_get_status($this->dvbprc[$name]);
+ if (!$procstatus['running'])
+ {
+ break;
+ }
+ sleep(1);
+ echo '.';
+ }
+ if ($procstatus['running'])
+ {
+ proc_terminate ($this->dvbprc[$name],SIGTERM);
+ echo "FAIL\nWARNING: The process '{$name}' did not complete normally.\n";
+ }
+ else
+ {
+ echo "DONE\n";
+ }
+ stream_get_contents($this->PIPES[$name][1]);
+ stream_get_contents($this->PIPES[$name][2]);
+ foreach ($this->PIPES[$name] as $procpipe)
+ {
+ fclose($procpipe);
+ }
+ proc_close($this->dvbprc[$name]);
+ unset($this->PIPES[$name], $this->dvbprc[$name], $this->runcmd[$name], $this->errbuffer[$name]);
+ $this->wdlock = FALSE;
+ }
+
+ public function UpdateMsgBuffer()
+ {
+ if ($this->wdlock)
+ {
+ return;
+ }
+ foreach ($this->PIPES as $name => $PIPE)
+ {
+ do
+ {
+ $str = fgets($PIPE[2]);
+ $ParsedStr = explode(':', $str, 2);
+ if ($ParsedStr[0] == 'error')
+ {
+ if (isset($ParsedStr[1]))
+ $this->errbuffer[$name][] = ltrim (rtrim ($ParsedStr[1]));
+ }
+
+ }
+ while ($str !== FALSE);
+ }
+ }
+
+ protected function GetMsgBuffer($name)
+ {
+ $messages = $this->errbuffer[$name];
+ $this->errbuffer[$name] = [];
+ return $messages;
+ }
+
+ public function ResetRestartFlag($name)
+ {
+ unset ($this->RestartFlag[$name]);
+ }
+
+ public function WatchdogDvblast()
+ {
+ if ($this->wdlock)
+ {
+ return;
+ }
+ foreach ($this->dvbprc as $name => $dvbprcres)
+ {
+ $procstatus = proc_get_status($dvbprcres);
+ if (!$procstatus['running'])
+ {
+ $this->UpdateMsgBuffer();
+ $errbuff = $this->GetMsgBuffer($name);
+ if (isset($this->RestartFlag[$name]))
+ {
+ echo "WARNING: The process '{$name}' unexpectedly crashed repeatedly! Abort respawn procedure.\n";
+ $this->StopDvblast($name);
+ }
+ else
+ {
+ $this->UpdateMsgBuffer();
+ $this->GetMsgBuffer($name);
+ echo "WARNING: The process '{$name}' unexpectedly crashed. Try to respawn...\n";
+ $runcmd = $this->runcmd[$name];
+ $this->StopDvblast($name);
+ $this->StartDvblast($name, $runcmd);
+ $this->RestartFlag[$name] = TRUE;
+ new Timer(8, [$this, 'ResetRestartFlag'],[$name], NULL);
+ new Timer(5, [$this, 'WatchdogDvblast'], NULL, NULL);
+ }
+ if (count($errbuff) > 0)
+ {
+ echo "The process '{$name}' returns the following error messages:\n";
+ echo implode("\n", $errbuff),"\n";
+ }
+ }
+ }
+ }
+
+ protected function loop()
+ {
+
+ $this->EventProcessor();
+ }
+
+ protected function PrepareDvblastParams ($_CONFIG)
+ {
+ $shellcmd = [];
+ $commonparams = '';
+ foreach ($_CONFIG['WorkCommonParams'] as $option => $value)
+ {
+ switch ($option)
+ {
+ case 'es_timeout':
+ $commonparams .= '-7 '.(integer)$value.' ';
+ break;
+ case 'provider_name':
+ $commonparams .= '-B '.$value.' ';
+ break;
+ case 'network_id':
+ $commonparams .= '-N '.(integer)$value.' ';
+ break;
+ case 'network_name':
+ $commonparams .= '-M '.$value.' ';
+ break;
+ case 'ttl':
+ if ($value > 1 && $value <256)
+ {
+ $commonparams .= '-t '.(integer)$value.' ';
+ }
+ else
+ {
+ echo "ERROR: Invalid 'ttl' options value in config, default value will be used.\n";
+ }
+ break;
+ case 'pidmap':
+ $commonparams .= '-0 '.$value.' ';
+ break;
+ case 'use_rtp':
+ if ($value === FALSE)
+ {
+ $commonparams .= '-U ';
+ }
+ break;
+ case 'epg_passthrough':
+ if ($value === TRUE)
+ {
+ $commonparams .= '-e ';
+ }
+ break;
+ }
+ }
+ foreach ($_CONFIG['WorkAdaptersCfg'] as $AdapterName => $AdapterCfg)
+ {
+ if ($AdapterCfg['MUX'] === FALSE)
+ {
+ continue;
+ }
+ $shellcmd[$AdapterName] = $_CONFIG['WorkCommonParams']['dvblast_path'].' ';
+ foreach ($AdapterCfg as $option => $value)
+ {
+ switch ($option)
+ {
+ case 'ADAPTER_NUM':
+ $shellcmd[$AdapterName] .= '-a '.(integer)$value.' ';
+ break;
+ case 'FRONTEND_NUM':
+ $shellcmd[$AdapterName] .= '-n '.(integer)$value.' ';
+ break;
+ }
+ }
+ foreach ($_CONFIG['Multiplexes'][$AdapterCfg['MUX']] as $option => $value)
+ {
+ switch ($option)
+ {
+ case 'delivery_system':
+ $shellcmd[$AdapterName] .= '-5 '.$value.' ';
+ break;
+ case 'frequency':
+ $shellcmd[$AdapterName] .= '-f '.(integer)$value.' ';
+ break;
+ case 'symbol_rate':
+ $shellcmd[$AdapterName] .= '-s '.(integer)$value.' ';
+ break;
+ case 'modulation':
+ $shellcmd[$AdapterName] .= '-m '.$value.' ';
+ break;
+ case 'plp_id':
+ $shellcmd[$AdapterName] .= '-9 '.(integer)$value.' ';
+ break;
+ case 'symbol_rate':
+ $shellcmd[$AdapterName] .= '-s '.(integer)$value.' ';
+ break;
+ case 'out':
+ $filecfgname = '/tmp/'.$AdapterName.'.dvbmcfg';
+ $filecfgdata = implode("\n", $value)."\n";
+ file_put_contents($filecfgname, $filecfgdata);
+ $shellcmd[$AdapterName] .= '-c '.$filecfgname.' ';
+ break;
+ }
+ }
+ $shellcmd[$AdapterName] .= $commonparams.'-q -r /tmp/dvb-'.$AdapterName.'.sock';
+ }
+ return $shellcmd;
+ }
+}
+
+
+$PRG = new dvbmaster();
--- /dev/null
+<?php\r
+\r
+/*\r
+ * VERSION 2.1b\r
+ */\r
+define('MPRole_PARENT', 0);\r
+define('MPRole_CHILD', 1);\r
+define('MPRole_ERROR', -1);\r
+\r
+class Daemon\r
+{\r
+ private static $unique_flag = FALSE;\r
+ private static $pidfilename;\r
+ \r
+ public static function shutdown()\r
+ {\r
+ if (self::$unique_flag)\r
+ {\r
+ @unlink(static::$pidfilename);\r
+ }\r
+ \r
+ }\r
+\r
+ public static function daemonize()\r
+ {\r
+ $pid = pcntl_fork();\r
+ if ($pid == -1)\r
+ {\r
+ return FALSE;\r
+ }\r
+ elseif ($pid > 0)\r
+ {\r
+ exit;\r
+ }\r
+ self::$origin_pid = posix_getpid();\r
+ umask(0);\r
+ chdir('/');\r
+ if (posix_setsid() == -1)\r
+ {\r
+ return FALSE;\r
+ }\r
+ \r
+ fclose(STDIN);\r
+ fclose(STDOUT);\r
+ fclose(STDERR);\r
+ $GLOBALS['STDIN'] = fopen('/dev/null', 'r');\r
+ $GLOBALS['STDOUT']= fopen('/dev/null', 'w');\r
+ $GLOBALS['STDERR'] = fopen('/dev/null', 'w');\r
+ \r
+ return TRUE;\r
+ }\r
+ \r
+ public static function is_unique($pidfilename)\r
+ {\r
+ $mypid = posix_getpid();\r
+ if (is_readable($pidfilename))\r
+ {\r
+ $pid = (int) rtrim(file_get_contents($pidfilename));\r
+ if ($pid == $mypid)\r
+ {\r
+ return TRUE;\r
+ }\r
+ if ($pid > 0 && posix_kill($pid, 0))\r
+ {\r
+ return FALSE;\r
+ }\r
+ }\r
+ return TRUE;\r
+\r
+ }\r
+ \r
+ public static function no_unique($pidfilename)\r
+ {\r
+ if (!@unlink($pidfilename))\r
+ {\r
+ return FALSE;\r
+ }\r
+ else\r
+ {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ public static function do_unique($pidfilename)\r
+ {\r
+ if (!self::is_unique($pidfilename))\r
+ {\r
+ return FALSE;\r
+ }\r
+\r
+ $mypid = posix_getpid();\r
+ if (!@file_put_contents($pidfilename, $mypid.PHP_EOL))\r
+ {\r
+ return FALSE;\r
+ }\r
+ register_shutdown_function(['self','shutdown']);\r
+ return TRUE;\r
+ }\r
+} \r
+\r
+abstract class Children \r
+{\r
+ protected $PIPE;\r
+ protected $ROLE;\r
+ protected static $CHILDREN;\r
+ protected $CHILD;\r
+ protected $PARENT;\r
+ protected $IPCRXBUFFER = [];\r
+\r
+\r
+ public function __construct($data = NULL)\r
+ {\r
+ self::set_sig_handlers();\r
+ $this->create_child();\r
+ switch ($this->ROLE)\r
+ {\r
+ case MPRole_ERROR:\r
+ return FALSE;\r
+ case MPRole_PARENT:\r
+ return TRUE;\r
+ case MPRole_CHILD:\r
+ $this->ChildBody($data);\r
+ exit();\r
+ }\r
+ \r
+ }\r
+ \r
+ public function __destruct()\r
+ {\r
+ if ($this->ROLE == MPRole_PARENT)\r
+ {\r
+ posix_kill($this->CHILD, SIGTERM);\r
+ }\r
+ }\r
+\r
+ abstract protected function ChildBody($data);\r
+\r
+ static function set_sig_handlers()\r
+ {\r
+ pcntl_signal(SIGCHLD, [__CLASS__, 'handler_sigchld']);\r
+ }\r
+ \r
+ public function SendUnixSignal($sig)\r
+ {\r
+ return posix_kill($this->CHILD, $sig);\r
+ }\r
+ \r
+ public function IsAlive()\r
+ {\r
+ if ($this->ROLE == MPRole_CHILD)\r
+ {\r
+ return TRUE;\r
+ }\r
+ if (isset(self::$CHILDREN[$this->CHILD]))\r
+ {\r
+ return TRUE;\r
+ }\r
+ else\r
+ {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ public static function handler_sigchld()\r
+ { \r
+ $pid = pcntl_waitpid(0, $status, WNOHANG);\r
+ if($pid > 0 && isset(self::$CHILDREN[$pid]))\r
+ {\r
+ unset(self::$CHILDREN[$pid]);\r
+ }\r
+ }\r
+\r
+ protected function create_child()\r
+ {\r
+ $sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);\r
+ stream_set_blocking ($sockets[0] , 0);\r
+ stream_set_blocking ($sockets[1] , 0);\r
+ $pid = pcntl_fork();\r
+ if ($pid == -1)\r
+ {\r
+ $this->ROLE = MPRole_ERROR;\r
+ return FALSE;\r
+ }\r
+ elseif ($pid > 0)\r
+ {\r
+ // код для родителя\r
+ $this->CHILD = $pid;\r
+ self::$CHILDREN[$pid] = $pid;\r
+ $this->PARENT = FALSE;\r
+ $this->ROLE = MPRole_PARENT;\r
+ $this->PIPE = &$sockets[0];\r
+ unset($sockets);\r
+ usleep(5000);\r
+ }\r
+ elseif ($pid == 0)\r
+ {\r
+ // код для ребёнка\r
+ $this->CHILD = FALSE;\r
+ $this->PARENT = posix_getppid();\r
+ $this->ROLE = MPRole_CHILD;\r
+ $this->PIPE = &$sockets[1];\r
+ unset($sockets);\r
+ }\r
+ return TRUE;\r
+ }\r
+ \r
+ public function SendEvent($eventname, $data)\r
+ {\r
+ if (!is_resource($this->PIPE))\r
+ {\r
+ return FALSE;\r
+ }\r
+ $eventpack = base64_encode($eventname).chr(255).chr(0).chr(255).base64_encode(serialize($data)).chr(0).chr(15).chr(240).chr(255);\r
+ \r
+ $res = @stream_socket_sendto($this->PIPE, $eventpack); \r
+ if ($res == -1)\r
+ {\r
+ return TRUE;\r
+ }\r
+ else\r
+ {\r
+ return FALSE;\r
+ }\r
+ }\r
+ \r
+ public function WaitEvent($wait = TRUE)\r
+ {\r
+ if (count($this->IPCRXBUFFER) == 0)\r
+ {\r
+ if (!is_resource($this->PIPE))\r
+ {\r
+ return FALSE;\r
+ }\r
+\r
+ $r = [$this->PIPE];\r
+ if ($wait)\r
+ {\r
+ while (stream_select($r, $w, $x, 0) == 0)\r
+ {\r
+ usleep(10000);\r
+ $r = [$this->PIPE];\r
+ }\r
+ }\r
+ elseif (stream_select($r, $w, $x, 0) == 0)\r
+ {\r
+ return NULL;\r
+ }\r
+ $ipc_msg = '';\r
+ while (substr($ipc_msg , -4) != chr(0).chr(15).chr(240).chr(255))\r
+ {\r
+ $rcv_buf = stream_socket_recvfrom($this->PIPE, 1500);\r
+ if ($rcv_buf == '')\r
+ {\r
+ return NULL;\r
+ }\r
+ $ipc_msg .= $rcv_buf;\r
+ }\r
+ $ipc_msg = explode(chr(0).chr(15).chr(240).chr(255), $ipc_msg);\r
+ array_pop($ipc_msg);\r
+ $this->IPCRXBUFFER = array_merge($this->IPCRXBUFFER, $ipc_msg);\r
+ }\r
+ $ipc_msg = array_shift($this->IPCRXBUFFER);\r
+ $ipc_msg = explode(chr(255).chr(0).chr(255), $ipc_msg);\r
+ \r
+ $retval['eventname'] = base64_decode($ipc_msg[0]);\r
+ $retval['data'] = unserialize(base64_decode($ipc_msg[1]));\r
+ \r
+ return $retval;\r
+ \r
+ }\r
+}\r
--- /dev/null
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of inigen
+ *
+ * @author sleepy
+ */
+class STS
+{
+ private static $SigArray = FALSE;
+ private static $IsPrefixed = FALSE;
+ private static $IsLastEol;
+ private static $StdPrefix = '';
+
+ public static function generate_ini($ini_array)
+ {
+ if (!is_array($ini_array))
+ {
+ return FALSE;
+ }
+ if (count($ini_array) == 0)
+ {
+ return NULL;
+ }
+ $_RETVAL= '';
+ if (is_array($ini_array[key ($ini_array)]))
+ {
+ foreach ($ini_array as $sectionname => $sectiondata)
+ {
+ if(!is_array($sectiondata))
+ {
+ return FALSE;
+ }
+ $_RETVAL .= '['.$sectionname.']'.PHP_EOL;
+ foreach ($sectiondata as $param => $data)
+ {
+ if (is_array($data))
+ {
+ $data = '';
+ }
+ $_RETVAL .= $param.' = '.$data.PHP_EOL;
+ }
+ $_RETVAL .= PHP_EOL;
+ }
+ }
+ else
+ {
+ foreach ($ini_array as $param => $data)
+ {
+ $_RETVAL .= $param.' = '.$data.PHP_EOL;
+ }
+ }
+ return $_RETVAL;
+ }
+ private static function GetSignalDescription()
+ {
+ if (self::$SigArray === FALSE)
+ {
+ self::$SigArray = explode("\n", shell_exec('kill -l'));
+ }
+ }
+
+ public static function GetSignalMnemonicByNumber($signo)
+ {
+ self::GetSignalDescription();
+ if (isset(self::$SigArray[$signo]))
+ {
+ return self::$SigArray[$signo];
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ public static function GetSignalNumberByMnemonic($sigmnemo)
+ {
+ self::GetSignalDescription();
+ return array_search($sigmnemo, self::$SigArray);
+ }
+
+ public static function stdout_prefix($prefix)
+ {
+ if (!self::$IsPrefixed)
+ {
+ self::$StdPrefix = $prefix;
+ ob_start(['self','ob_callback'],1);
+ self::$IsPrefixed = TRUE;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ }
+
+ public static function stdout_unprefix()
+ {
+ if (self::$IsPrefixed)
+ {
+ ob_end_clean();
+ self::$IsPrefixed = FALSE;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ }
+
+ private static function ob_callback($buffer)
+ {
+ if (self::$IsLastEol)
+ {
+ $prefix = self::$StdPrefix;
+ }
+ else
+ {
+ $prefix = '';
+ }
+ self::$IsLastEol = substr($buffer, -1) == PHP_EOL;
+ return $prefix.$buffer;
+
+ }
+
+}
+
--- /dev/null
+<?php
+//VERSION 2.4
+register_tick_function(array('Timer','poll'));
+
+class Timer
+{
+ private $id; //метка
+ private $time; //таймер
+ private $IsStop = TRUE; //флаг остановленности true/false
+ private $UserCall; //задача
+ private $UserCallArg; //параметр к задаче в виде массива для передачи нескольких значений или единственного.
+ private $StartTime; //момент запуска таймера
+ private $ltime; //оставшееся время
+ private $type; //тип true регенерируемый, false не регенерируемый, null самоуничтожаемый не регенерируемый, применяется по умолчанию без присвоения, не может быть перезапущен.
+ private static $timers = [];
+
+ public function __construct($time, $callable, $args = NULL, $type = NULL)
+ {
+ if ($args === NULL)
+ {
+ $args = [];
+ }
+ if (!is_array($args))
+ {
+ throw new Exception("parameter 3 must be an array, \$args=".gettype($args));
+ }
+ $this->id = uniqid();
+ $this->time = $time;
+ $this->UserCall = $callable;
+ $this->UserCallArg = $args;
+ $this->type = $type;
+ if (!is_callable($this->UserCall, TRUE, $callablename))
+ {
+ throw new Exception("parameter 2 must be callable, \$callable=". $callablename);
+ }
+ else
+ {
+ $this->start();
+ self::$timers[$this->id] = &$this;
+ }
+ }
+
+ public function __destruct()
+ {
+ unset(self::$timers[$this->id]);
+ }
+
+ private function getId()
+ {
+ return $this->id;
+ }
+
+ public function getTimeLeft()
+ {
+ return $this->ltime;
+ }
+
+ public function setTimeLeft($time)
+ {
+ $this->time = $time;
+ }
+
+
+ public function reset()
+ {
+ $this->StartTime = microtime(true);
+ $this->IsStop = FALSE;
+ }
+
+ public function start()
+ {
+ $this->reset();
+ $this->IsStop = FALSE;
+ }
+
+ public function stop()
+ {
+ $this->IsStop = TRUE;
+ }
+
+ public static function poll()
+ {
+ foreach (self::$timers as $timer)
+ {
+ if ($timer->IsStop == FALSE)
+ {
+ $timer->update();
+ }
+ }
+ }
+
+ private function update()
+ {
+ $this->ltime = $this->StartTime - microtime(true) + $this->time;
+ if ($this->ltime <= 0)
+ {
+ $this->ltime = 0;
+ $this->task();
+ if ($this->type === FALSE)
+ {
+ $this->stop();
+ }
+ elseif ($this->type === TRUE)
+ {
+ $this->reset();
+ }
+ elseif ($this->type === NULL)
+ {
+ $this->stop();
+ $this->__destruct();
+ }
+ }
+ }
+
+ private function task()
+ {
+ call_user_func_array ($this->UserCall, $this->UserCallArg);
+ }
+}
--- /dev/null
+<?php
+$tarr1 = [1,2,3,4];
+$tarr2 = [1,2,3,4];
+$array1 = ['a' => 1, 'b' => 2, 'c' => $tarr1];
+$array2 = ['a' => 1, 'b' => 2, 'c' => $tarr2];
+//var_dump($tarr1 == $array2['c']);
+//var_dump($array1 == $array2);
+
+$result = array_udiff_assoc($array1, $array2, function ($a, $b) {return (int)!($a === $b);});
+
+function cmpre($param1, $param2)
+{
+ var_dump($param1,$param2);
+ echo '------',PHP_EOL;
+ return 1;
+}
+
+var_dump($result);
+exit();
+var_dump($a);
+if (($a = 5) < 1)
+{
+ echo 'TRUE',PHP_EOL;
+}
+else
+{
+ echo 'FALSE',PHP_EOL;
+}
+var_dump($a);
+exit();
+// Обязательно
+declare(ticks = 1);
+
+// функция обработки сигнала
+function sig_handler($signo,$signinfo)
+{
+
+
+ file_put_contents('/tmp/sigtest.txt', "PID:".posix_getpid()." GOT SIGNAL {$signo} ".print_r($signinfo,true)."\n", FILE_APPEND);
+
+
+
+}
+
+echo "Установка обработчиков сигналов...\n";
+
+// Установка обработчиков сигналов
+pcntl_signal(SIGTERM, "sig_handler");
+pcntl_signal(SIGHUP, "sig_handler");
+pcntl_signal(SIGABRT, "sig_handler");
+pcntl_signal(SIGINT, "sig_handler");
+pcntl_signal(SIGQUIT, "sig_handler");
+pcntl_signal(SIGINT, "sig_handler");
+//pcntl_signal(SIGSTOP, "sig_handler");
+
+sleep(3);
+sleep(3);
+
+//exit();
+// или можете использовать объект
+// pcntl_signal(SIGUSR1, array($obj, "do_something"));
+
+while (1)
+{
+ echo '*';
+ sleep(1);
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of lowterm
+ *
+ * @author sleepy
+ */
+class lowterm
+{
+ protected $terminfo;
+
+ public function __construct()
+ {
+
+ }
+}
--- /dev/null
+#!/usr/bin/env php
+<?php
+require_once 'lowterm.php';
+exec('infocmp -L -1', $output);
+unset($output[0],$output[1]);
+foreach ($output as $value)
+{
+ $kp = explode('=', rtrim(ltrim($value),','));
+ if (!isset($kp[1]))
+ {
+ $kp[1] = TRUE;
+ }
+ $attr[$kp[0]] = $kp[1];
+
+}
+echo str_replace ('\E', chr(27), $attr["clear_screen"]);
+var_dump($attr);
+echo chr(27).'[425m';
\ No newline at end of file