I am making an html5 game www.titansoftime.com
I am using ratchet as a php websocket server solution. It works great! http://socketo.me/docs/push
I have done several standalone test using the php pthreads extension and have seen some very exciting results. It truly works and works well.. as long as websockets aren't in the mix.
Pthreads give php multithreading capabilities (it really does work and it's amazing). http://php.net/manual/en/book.pthreads.php
This is what I do:
/src/server.php
This is the file that launches the daemon.
<?php
session_start();
use Ratchet\Server\IoServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Pusher;
require __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../mysql.cls.php';
require_once __DIR__ . '/../game.cls.php';
require_once __DIR__ . '/../model.cls.php';
$mysql = new mysql;
$game = new game;
$loop = React\EventLoop\Factory::create();
$pusher = new MyApp\Pusher();
$loop->addPeriodicTimer(0.50, function() use($pusher){
$pusher->load();
});
$webSock = new React\Socket\Server($loop);
if ($loop instanceof \React\EventLoop\LibEventLoop) {
echo "\n HAS LibEvent";
}
$webSock->listen(8080, '0.0.0.0'); // Binding to 0.0.0.0 means remotes can connect
$webServer = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer(
new Ratchet\WebSocket\WsServer($pusher)
),
$webSock
);
$loop->run();
This all works fine.
/src/MyApp/Pusher.php
This class pushes data to all connected users.
<?php
namespace MyApp;
use Ratchet\ConnectionInterface;
use Ratchet\MessageComponentInterface;
class AsyncThread extends \Thread{
public $client;
public function __construct($client){
$this->client = $client;
}
public function run(){
// do work on $this->client
$user = mysql::assoc('SELECT * from users WHERE connection_id = "'.$this->client->resourceId.'"');
// etc..
$this->client->send(json_encode(array('foo'=>'bar')));
}
}
class Pusher implements MessageComponentInterface{
public static $clients = array();
#load
public static function load(){
$client_count = count(self::$clients);
echo "\n\n\n".'Serving to '.$client_count.' clients. '.time();
$start = $istart = microtime(true);
if( !count(self::$clients) ){
if( !mysql_ping() ){
$game->connect();
}
}
$threads = array();
foreach( self::$clients as $key => $client ){
// HANDLE CLIENT
// This works just fine, the only problem is that if I have lets say 50 simultaneous users, the people near the end of the clients array will have to wait till the other users have been processed. This is not desirable
$client->send(json_encode('foo'=>'bar'));
// So I tried this:
$threads[$key] = new AsyncThread($client);
$threads[$key]->start();
// At this point the AsyncThread class will throw a fatal error complaining about not being able to serialize a closure.
// If I dont set "$this->data = $client;" in the thread constructor no error appears but now I cant use the data.
// Also regardless of whether or not I bind the data in the AsyncThread constructor,
// the connection disappears if I call "new AsyncThread($client)". I cannot explain this behavior.
}
}
public function onMessage(ConnectionInterface $from, $msg) {
global $game;
if( $msg ){
$data = json_decode($msg);
if( $data ){
switch( $data->task ){
#connect
case 'connect':
echo "\n".'New connection! ('.$from->resourceId.') '.$from->remoteAddress;
self::$clients[] = $from;
break;
default:
self::closeConnection($from);
echo "\nNO TASK CLOSING";
break;
}
}else{
echo "\n NO DATA";
self::closeConnection($from);
}
}else{
echo "\n NO MSG";
self::closeConnection($from);
}
}
public function closeConnection($conn){
global $game;
if( $conn ){
if( $conn->resourceId ){
$connid = $conn->resourceId;
$conn->close();
$new = array();
foreach( self::$clients as $client ){
if( $client->resourceId != $connid ){
$new[] = $client;
}
}
self::$clients = $new;
$game->query('UPDATE users set connection_id = 0 WHERE connection_id = "'.intval($connid).'" LIMIT 1');
echo "\n".'Connection '.$connid.' has disconnected';
}
}
}
public function onClose(ConnectionInterface $conn) {
echo "\nCLIENT DROPPED";
self::closeConnection($conn);
}
public function onOpen(ConnectionInterface $conn) {
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "\nCLIENT ERRORED";
self::closeConnection($conn);
}
public function onSubscribe(ConnectionInterface $conn, $topic) {
}
public function onUnSubscribe(ConnectionInterface $conn, $topic) {
}
public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {
}
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
}
}
This all works fine as long as I don't create a thread inside the event loop.
Am I going about this the wrong way or is php multithreading and websockets incompatible?
check this package https://github.com/huyanping/react-multi-process
Install
composer require jenner/react-multi-process
How to use it?
So simple like:
$loop = React\EventLoop\Factory::create();
$server = stream_socket_server('tcp://127.0.0.1:4020');
stream_set_blocking($server, 0);
$loop->addReadStream($server, function ($server) use ($loop) {
$conn = stream_socket_accept($server);
$data = "pid:" . getmypid() . PHP_EOL;
$loop->addWriteStream($conn, function ($conn) use (&$data, $loop) {
$written = fwrite($conn, $data);
if ($written === strlen($data)) {
fclose($conn);
$loop->removeStream($conn);
} else {
$data = substr($data, 0, $written);
}
});
});
// the second param is the sub process count
$master = new \React\Multi\Master($loop, 20);
$master->start();
An example using jenner/simple_fork like:
class IoServer {
/**
* #param int $count worker process count
* Run the application by entering the event loop
* #throws \RuntimeException If a loop was not previously specified
*/
public function run($count = 1) {
if (null === $this->loop) {
throw new \RuntimeException("A React Loop was not provided during instantiation");
}
if($count <= 1){
$this->loop->run();
}else{
$loop = $this->loop;
$master = new \Jenner\SimpleFork\FixedPool(function() use($loop) {
$this->loop->run();
}, $count);
$master->start();
$master->keep(true);
// or just
// $master = new \React\Multi\Master($this->loop, $count);
// $master->start();
}
}
}
Related
The objective is to continually collect data of the current temperature. But a separate process should analyse the output of that data because I have to tweak the algorithm a lot but I want to avoid downtime so stopping the process is a no-go.
The problem is when I separate these processes, that process 2 would either continually have to make calls to the database or read from a local file to do something with the output generated by 1 process but I want to act upon it immediately and that is expensive in terms of resources.
Would it be possible to reload the class into memory somehow when the file changes by for example writing a function that keeps calculating the MD5 of the file, and if it changes than reload the class somehow? So this separate class should act as a plugin. Is there any way to make that work?
Here is a possible solution. Use Beanstalk (https://github.com/kr/beanstalkd).
PHP class to talk to Beanstalk (https://github.com/pda/pheanstalk)
Run beanstalk.
Create a process that goes into an infinite loop that reads from a Beanstalk queue. (Beanstalk queues are called "Tubes"). PHP processes are not meant to be run for a very long time. The main reason for this is memory. The easiest way to do handle this is to restart the process every once in a while or if memory gets to a certain threshold.
NOTE: What I do is to have the process exit after some fixed time or if it uses a certain amount of memory. Then, I use Supervisor to restart it.
You can put data into Beanstalk as JSON and decode it on the receiving end. The sending and receiving processes need to agree on that format. You could store your work payload in a database and just send the primary key in the queue.
Here is some code you can use:
class BeanstalkClient extends AbstractBaseQueue{
public $queue;
public $host;
public $port;
public $timeout;
function __construct($timeout=null) {
$this->loadClasses();
$this->host = '127.0.0.1';
$this->port = BEANSTALK_PORT;
$this->timeout = 30;
$this->connect();
}
public function connect(){
$this->queue = new \Pheanstalk\Pheanstalk($this->host, $this->port);
}
public function publish($tube, $data, $delay){
$payload = $this->encodeData($data);
$this->queue->useTube($tube)->put($payload,
\Pheanstalk\PheanstalkInterface::DEFAULT_PRIORITY, $delay);
}
public function waitForMessages($tube, $callback=null){
if ( $this->timeout ) {
return $this->queue->watchOnly($tube)->reserve($this->timeout);
}
return $this->queue->watchOnly($tube)->reserve();
}
public function delete($message){
$this->queue->delete($message);
}
public function encodeData($data){
$payload = json_encode($data);
return $payload;
}
public function decodeData($encodedData) {
return json_decode($encodedData, true);
}
public function getData($message){
if ( is_string($message) ) {
throw new Exception('message is a string');
}
return json_decode($message->getData(), true);
}
}
abstract class BaseQueueProcess {
protected $channelName = ''; // child class should set this
// The queue object
public $queue = null;
public $processId = null; // this is the system process id
public $name = null;
public $status = null;
public function initialize() {
$this->processId = getmypid();
$this->name = get_called_class();
$this->endTime = time() + (2 * 60 * 60); // restart every hour
// seconds to timeout when waiting for a message
// if the process isn't doing anything, timeout so they have a chance to do housekeeping.
$queueTimeout = 900;
if ( empty($this->queue) ) {
$this->queue = new BeanstalkClient($queueTimeout);
}
}
public function receiveMessage($queueMessage) {
$taskData = $this->queue->getData($queueMessage);
// debuglog(' Task Data = ' . print_r($taskData, true));
if ( $this->validateTaskData($taskData) ) {
// process the message
$good = $this->didReceiveMessage($taskData);
if ( $good !== false ) {
// debuglog("Completing task {$this->taskId}");
$this->completeTask($queueMessage);
}
else {
$this->failTask($queueMessage);
}
}
else {
// Handle bad message
$this->queue->delete($queueMessage);
}
}
public function run() {
$this->processName = $this->channelName;
// debuglog('Start ' . $this->processName);
// debuglog(print_r($this->params, true));
while(1) {
$queueMessage = $this->queue->waitForMessages($this->channelName);
if ( ! empty($queueMessage) ) {
$this->receiveMessage($queueMessage);
}
else {
// empty message
// a timeout
// // debuglog("empty message " . get_called_class());
}
$memory = memory_get_usage();
if( $memory > 20000000 ) {
// debuglog('Exit '.get_called_class().' due to memory. Memory:'. ($memory/1024/1024).' MB');
// Supervisor will restart process.
exit;
}
elseif ( time() > $this->endTime ) {
// debuglog('Exit '.get_called_class().' due to time.');
// Supervisor will restart process.
exit;
}
usleep(10);
}
}
public function completeTask($queueMessage) {
//
$this->queue->delete($queueMessage);
}
public function failTask($queueMessage) {
//
$this->queue->delete($queueMessage);
}
}
class MyProcess extends BaseQueueProcess {
public function initialize() {
$this->channelName = 'Temperature';
parent::initialize();
}
public function didReceiveMessage($taskData) {
// debuglog(print_r($taskData, true));
// process data here
// return false if something went wrong
return true;
}
}
//Sender
class WorkSender {
const TubeName = 'Temperature';
const TubeDelay = 0; // Set delay to 0, i.e. don't use a delay.
function send($data) {
$c = BeanstalkClient();
$c->publish(self::TubeName, $data, self::TubeDelay);
}
}
When i run gearman upto three workers on single server is working fine but when i start the 4th one php code is working fine but can't detect the new worker and also not clearing the job queue.
protected function createWorker()
{
$this->worker = new \GearmanWorker();
$config = $this->app->config->job_remote;
$this->worker->addServer($config['host'], $config['port']);
return $this->worker;
}
public function listen($eventType, $callback)
{
if (!($this->worker instanceof \GearmanWorker)){
$this->worker = $this->createWorker();
}
$this->worker->addFunction($eventType, $callback);
return $this->worker;
}
public function doWork($worker)
{
if (!($worker instanceof \GearmanWorker)){
$worker = $this->createWorker();
}
$this->worker = $worker;
while (1) {
$this->worker->work();
$this->app->log->debug($this->worker->returnCode());
if ($this->worker->returnCode() != \GEARMAN_SUCCESS) {
break;
}
}
}
First I am calling 'listen' method and then 'doWork' method
Client Side Code:
protected function createClient()
{
$this->client = new \GearmanClient();
$config = $this->app->config->job_remote;
$this->client->addServer($config['host'], $config['port']);
return $this->client;
}
public function addTask($eventType, array $params)
{
if (!($this->client instanceof \GearmanClient)){
$this->client = $this->createClient();
}
// add single task in queue
$this->client->addTaskBackground($eventType, serialize($params));
// Run task
$this->client->runTasks();
}
Using GearmanManager You can run upto any no. of workers untill it slows down your computer and uses 100% cpu and 100% memory.
Gearman.ini
[GearmanManager]
worker_dir=./workers
count=50
dedicated_count=1
max_worker_lifetime=3600
auto_update=1
log_file=./logs/WebAnalyzerWorker.log
max_runs_per_worker=3
timeout=600
Run Gearman worker
./vendor/brianlmoon/gearmanmanager/pecl-manager.php -c ./gearman.ini -vvvvv
Worker CLass
<?php
include dirname(__DIR__)."/vendor/autoload.php";
use Services\Log;
use Services\ServiceInitialization;
use Lib\Technology\FindWhoisRecords;
use Illuminate\Database\Capsule\Manager as Capsule;
class DomainDetailFetchJob{
public function beforeRun()
{
ServiceInitialization::loadConfig();
ServiceInitialization::loadDatabaseConfiguration();
}
public function run($job, &$log)
{
$log[] = "Starting whois job";
$collection = "whois";
$this->beforeRun();
ServiceInitialization::$config->all();
$workload = $job->workload();
//workload
$whois = new FindWhoisRecords($workload);
if($whois->whois_result["err_code"]==0) {
$log[] = "Whois Info fetch successful";
//success save the details
$whois->whois_result["result"]["workload"] = $workload;
Capsule::table($collection)->where("workload", $workload)->update($whois->whois_result["result"], ['upsert' => true]);
}
else {
$log[] = "Whois Info fetch failed";
$logger = new Log();
$logger->write($whois->whois_result["err_msg"], "error", "Whois Record Job");
unset($logger);
}
}
}
Gearman Client
$client = new GearmanClient();
$client->addServer();
$client->doBackground("DomainDetailFetchJob", $url);
I have to subscribe to all the channels of my Redis db and simultaneously read data from another hash in the same db node. The following is the code I have written for this using phpredis:
$notif = new Worker;
try {
// connect redis
$notif->connectRedisServer();
// start the worker processing
$notif->startWorkerProcess();
}
catch(Exception $e) {
exit('Exception: '.$e->getMessage());
}
class Worker {
private $redis;
public $recentHash = 'tracker.data.recent';
public $notifHash = 'tracker.settings.data';
public $serverConnectionDetails = { //Redis server connection details
};
private $redis;
// redis database used for getting recent record of every imei
const RECENTDATA_DB = 1;
public function connectRedisServer() {
if(!empty($this->redis))
return $this->redis; // already connected
$this->redis = new Redis();
if(!$this->redis->pconnect(
$this->serverConnectionDetails['redis']['host'],
$this->serverConnectionDetails['redis']['port']
))
return 'Redis connection failed.';
if(isset($this->serverConnectionDetails['redis']['password'])
&&
!$this->redis->auth($this->serverConnectionDetails['redis']['password'])
)
return 'Redis authentication failed.';
return $this->redis;
}
public function startWorkerProcess() {
$this->redis->select(self::RECENTDATA_DB);
$this->redis->pSubscribe(array('*'), array($this, 'processRedisData'));
}
public function processRedisData($redis, $pattern, $chan, $msg) {
$message = rtrim((string) $msg,",");
$tData = json_decode($message, true);
$tId = (int) $tData['tID'];
echo "Recent Published Data:";
var_dump($tData);
$data = $this->getNotifSettings($tId);
if(!empty($data)) {
echo "Redis Settings: ";
var_dump($trackerSettings);
}
}
public function getNotifSettings($tId) {
$data = $this->redis->hGet($this->notifHash, $tId); //This command doesn't return any data even if it exists for the $tId
if($data === false)
return null;
$data = json_decode($data, true);
return $data; // Always comes up as null
}
}
The problem here is that once I get subscribed to all the channels on db1 in Redis. I don't get any result if I try to run HGET, even though the data for the given key exists in the db. I have put additional comments in the code above to explain where the problem is. Check getNotifSettings() function.
Any help will be appreciated.
I'm trying to build a system where user can subscribe to a category on establishing connection to websocket server and then he will start receiving updates for that category. So far, I have worked with Ratchet and I am able to send message to all the connected clients but the problem is I don't want to send message to all the clients I just want to send the message to the clients who have subscribed the the particular category in which the messages was sent.
PHP Code
Chat.php
<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Chat implements MessageComponentInterface
{
protected $clients;
public function __construct()
{
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
}
public function onMessage(ConnectionInterface $conn, $msg)
{
foreach ($this->clients as $client)
{
if($conn !== $client)
$client->send($msg);
}
}
public function onClose(ConnectionInterface $conn)
{
$this->clients->detach($conn);
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
?>
server.php
<?php
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;
require dirname(__DIR__) . '/Ratchet/vendor/autoload.php';
$server = IoServer::factory(
new HttpServer(
new WsServer(
new Chat()
)
),
8080
);
$server->run();
?>
Client side js code
<script type="text/javascript">
var conn = new WebSocket('ws://localhost:8080');
conn.onopen = function(e) {
console.log("Connection established!");
};
conn.onmessage = function(e) {
console.log(e.data);
};
</script>
Basically you want a syntax for sending data to the WebSocket, I reccomend using a JSON object to do this. In your WebSocket class you need a local variable called subscriptions and a local variable called users like so.
<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Chat implements MessageComponentInterface
{
protected $clients;
private $subscriptions;
private $users;
public function __construct()
{
$this->clients = new \SplObjectStorage;
$this->subscriptions = [];
$this->users = [];
}
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
$this->users[$conn->resourceId] = $conn;
}
public function onMessage(ConnectionInterface $conn, $msg)
{
$data = json_decode($msg);
switch ($data->command) {
case "subscribe":
$this->subscriptions[$conn->resourceId] = $data->channel;
break;
case "message":
if (isset($this->subscriptions[$conn->resourceId])) {
$target = $this->subscriptions[$conn->resourceId];
foreach ($this->subscriptions as $id=>$channel) {
if ($channel == $target && $id != $conn->resourceId) {
$this->users[$id]->send($data->message);
}
}
}
}
}
public function onClose(ConnectionInterface $conn)
{
$this->clients->detach($conn);
unset($this->users[$conn->resourceId]);
unset($this->subscriptions[$conn->resourceId]);
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
?>
The javascript to go with that looks a bit like this
<script type="text/javascript">
var conn = new WebSocket('ws://localhost:8080');
conn.onopen = function(e) {
console.log("Connection established!");
};
conn.onmessage = function(e) {
console.log(e.data);
};
function subscribe(channel) {
conn.send(JSON.stringify({command: "subscribe", channel: channel}));
}
function sendMessage(msg) {
conn.send(JSON.stringify({command: "message", message: msg}));
}
</script>
Note: This code is untested I wrote it on the fly from my experience with Ratchet. Good luck :)
ok, now I am sharing my experience. You Can Send Token and Insert This toke in onOpen (server.php) in the database. You can Insert Chat Token When User Login. Every Time When User Login New Token is generated and updated.
Now Use This Token To Find out user Id in onOpen connection.
var conn = new WebSocket('ws://172.16.23.26:8080?token=<?php echo !empty($_SESSION['user']['token']) ? $_SESSION['user']['token'] : ''; ?>');
conn.onopen = function(e) {
console.log("Connection established!");
};
Now Server.php
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
$querystring = $conn->httpRequest->getUri()->getQuery();
parse_str($querystring,$queryarray);
//Get UserID By Chat Token
$objUser = new \users;
$objUser->setLoginToken($queryarray['token']);
$GetUserInfo = $objUser->getUserByToken();
echo "New connection! ({$conn->resourceId})\n";
//save Chat Connection
// Insert the new connection with the user ID to Match to later if user ID in Table Then Update IT with a new connection
}
Now your Connection is established, then send Message To particular users
Client-Side send USerID with received ID and Message
$("#conversation").on('click', '#ButtionID', function () {
var userId = $("#userId").val();
var msg = $("#msg").val();
var receiverId = $("#receiverId").val();
if ( userId != null && msg != null ){
var data = {
userId: userId,
msg: msg,
receiverId: receiverId
};
conn.send(JSON.stringify(data));
$("#msg").val("");
}
});
Server Side
public function onMessage(ConnectionInterface $from, $msg) {
$numRecv = count($this->clients) - 1;
echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n",$from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
$data = json_decode($msg, true);
echo $data['userId'];
echo $data['msg'];
echo $data['receiverId'];
// Save User ID receiver Id message in table
//Now Get Chat Connection by user id By Table Which is saved onOpen function
$UptGetChatConnection = $objChatroom->getChatConnectionUserID($data['receiverId']);
$ReciId = $UptGetChatConnection[0]['ConnectionID'];
foreach ($this->clients as $client) {
if ($from == $client) {
$data['from'] = "Me";
$data['fromname'] = 5;
} else {
$data['from'] = $user['name'];
$data['fromname'] = 6;
}
echo $client->resourceId;
echo $ReciId."\n";
if($client->resourceId == $ReciId || $from == $client){
$client->send(json_encode($data));
}
}
}
now the message has been sending by connection ID not broadcast only send particular
users
// SOCKET VARIABLE DECLARATION
var mySocket;
// MESSAGE
const socketMessageListener = function(event) {
var data = JSON.parse(event.data);
};
// OPEN SOCKET
const socketOpenListener = function(event) {
console.log('Connected');
};
// CLOSE OR DISCONNECT SOCKET
const socketCloseListener = function(event) {
if (mySocket) {
console.error('Disconnected.');
}
mySocket = new WebSocket('ws://localhost:8080');
mySocket.addEventListener('open', socketOpenListener);
mySocket.addEventListener('message', socketMessageListener);
mySocket.addEventListener('close', socketCloseListener);
};
// CALL THE LISTENER
socketCloseListener();
function sendMessage(data) {
mySocket.send(JSON.stringify(data));
}
user3049006 i found this code in my research to keep the connection persistent if fail retry the connection again.
I have a couple services that id like to have access to from a website. Like maybe a simple website with login for users and access to start|Stop|Restart services if they are down.
IDK if this is possible within a webpage as im not all that experienced yet.
From a shell i can just type out service b3-2 start and the service will start. now i just want to be able todo that from within a webpage where the start|Stop|Restart are buttons and there's a info/status area and an output box below everything to show errors if any.
If anyone can help me with this that would be awesome. Mostly my questions are-
Is it possible to do this from a webpage?
If so how hard will it be to create?
What else can you tell me to better help me with this?
on a side note il be using this for mostly only my B3 services(Big Brother Bot).
I wrote long time ago an 'Eggdrop Webinterface' in PHP with http://de.php.net/manual/en/book.ssh2.php Module.
You can connect with this module via SSH to your linux server and submit shell commands. In my Webinterface I wrote an Shellscript that do all the jobs (Add / remove / start / stop and so on) and executed over php ssh2 module.
<?php
class ssh
{
private $sql;
private $con;
private static $instance;
public static function getInstance()
{
if (!self::$instance)
{
self::$instance = new ssh();
}
return self::$instance;
}
function __construct()
{
$this->sql = sql::getInstance();
}
/* Verbindung über SSH aufnehmen */
public function connect ($rootid)
{
global $cfg;
$q = $this->sql->query("SELECT ROOT_SSH_IP, ROOT_SSH_USER, ROOT_SSH_PASS, ROOT_SSH_PORT FROM ".prfx."rootserver WHERE ROOT_ID = '".$rootid."'");
$r = $this->sql->content($q);
$blowfish = new Blowfish($cfg["BLOWFISHKEY"]);
$pass = $blowfish->Decrypt($r["ROOT_SSH_PASS"]);
$this->ssh_connect($r["ROOT_SSH_IP"], $r["ROOT_SSH_PORT"], $pass, $r["ROOT_SSH_USER"]);
return true;
}
/* Disconnect */
public function my_ssh_disconnect($reason, $message, $language) {
printf("Server disconnected with reason code [%d] and message: %s\n",
$reason, $message);
return true;
}
/* Eigentliche ssh_connect Funktion */
public function ssh_connect($host, $port, $pass, $user="root")
{
$methods = array(
'kex' => 'diffie-hellman-group1-sha1',
'client_to_server' => array(
'crypt' => '3des-cbc',
'comp' => 'none'),
'server_to_client' => array(
'crypt' => 'aes256-cbc,aes192-cbc,aes128-cbc',
'comp' => 'none'));
$callbacks = array();
$this->con = ssh2_connect($host, $port, $methods, $callbacks);
if (!$this->con) die('Connection failed');
else {
if (!ssh2_auth_password($this->con, $user, trim($pass))) {
die("login failed.");
}
}
return true;
}
/* Befehle ausführen */
public function ssh_exec ($cmd, $stderr=true)
{
if ($stderr == true)
{
$stream = ssh2_exec($this->con, $cmd);
$err_stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
stream_set_blocking($err_stream, true);
$result_err = stream_get_contents($err_stream);
if (empty($result_err))
{
stream_set_blocking($stream, true);
$out = stream_get_contents($stream);
return $out;
}
else {
return $result_err;
}
}
else {
$stream = ssh2_exec($this->con, $cmd);
stream_set_blocking($stream, true);
$out = stream_get_contents($stream);
return $out;
}
}
/* Verbindung beenden */
public function quit()
{
$stream = ssh2_exec($this->con, 'exit');
stream_set_blocking($stream, true);
$output = stream_get_contents($stream);
return true;
}
}
In my case I stored Serveraccounts in MySQL Table with encrypted password.
hope that little class helps
if you want to "write it yourself" you could mix bash,php and/or other scripting..