martedì 23 dicembre 2014

[PHP bash Centos] create a server with a PHP script and daemonize it

I started from the need to receive JSON data from some devices, parse them and put in MySQL. Nothing better than a PHP script to perform this job, except that you need to manually launch it via bash, your ssh access will timeout, you could also check it via crontab with CPU waste, etc etc.

So I just developed my server listening on a port, made it able to receive multiple client connections and daemonized it: now I can start, stop, restart, ps au -> kill it, cat a logfile, have it ready on system reboot and everything I would expect from a regularly compiled application written in C++.

BUT... I can modify it whenever I like without recompiling it, it natively talks with its companion website, etc etc.

I tried mpdaemon and several other solutions... I already tested them all for you: at the end, nothing better than the easy way.

Tested with CentOS but you can easily apply it to Ubuntu.

PHP SERVER:

#!/usr/bin/php

<?

####### PHP ERROR HANDLING #######
if (function_exists('ini_set')) {
ini_set('display_errors', '1');
ini_set('error_reporting', '7');
ini_set('max_execution_time',0);// INFINITE
ini_set("memory_limit", "300M");
} else
error_reporting(E_ALL & ~E_NOTICE);
/*----------------------------------*/

####### INITIALIZE VARIABLES #######
date_default_timezone_set("Europe/Rome");
$client_socks = array(); //WILL CONTAIN ALL CLIENT SOCKETS
$client_data = array(); //WILL CONTAIN CLIENTS IP:PORT just for debug & compliance
$port = 4000;
$log = '/var/log/myDAEMON.log';


####### INCLUDE MYSQL AND OTHER CONFIGS #######
include("config.php");
/*----------------------------------*/

####### MYSQL CONNECTION #######
mysql_connect($dbhost, $dbuser, $dbpass) or die(mysql_error());
mysql_select_db($dbname) or die(mysql_error());
mysql_query("SET CHARACTER SET utf8");
/*----------------------------------*/



//init socket
$socket = stream_socket_server("tcp://0.0.0.0:".$port, $errno, $errorMessage) or die("Could not bind to socket: $errorMessage");

//fork the process to work in a daemonized environment
file_put_contents($log, date("d.m.y H:i",time())." starting up.\n", FILE_APPEND);
$pid = pcntl_fork();
if($pid == -1) {
file_put_contents($log, "Error: could not daemonize process.\n", FILE_APPEND);
return 1; //error
} else if($pid) {
return 0; //success
} else {

    ##############################
    //THIS IS WHERE YOU SHOULD PLACE ANY MYSQL CONNECTION
    //since it's after the fork (it will receive a new PID)
    //else you will get crazy debugging the 2006 error "mysql server has gone"
mysql_connect($dbhost, $dbuser, $dbpass) or die(mysql_error());
mysql_select_db($dbname) or die(mysql_error());
mysql_query("SET CHARACTER SET utf8");
    ##############################

    ##############################
    //the main process
    while(true) {

        //prepare readable sockets array
        $read_socks = $client_socks; //will be filled below and then looped
        $read_socks[] = $socket; //server starts here

//select all sockets one by one and apply long timeout
stream_select ( $read_socks, $write, $except, 300000 ) or die('something went wrong while selecting a socket');

//check for new connected clients
if (in_array($socket, $read_socks)) {
$new_client = stream_socket_accept($socket);

if ($new_client) {
//remote client info, ip, port number
$client_info = strtolower( stream_socket_get_name($new_client, true) );
echo "Connection accepted from " . $clientinfo . "\n";

//add it in array client_socks
$client_socks[] = $new_client;
$client_data[] = $client_info;
echo "Now there are total ". count($client_socks) . " clients.\n";
}

//clean read_socks
unset($read_socks[ array_search($socket, $read_socks) ]);
}

//messagges from the clients
foreach($read_socks as $sock) {

//whom client speaking to?
$clientkey = array_search($sock, $client_socks);

//here I read the stream
$data = fread($sock, 128);

if(!$data) {
unset($client_socks[ $clientkey ]);
unset($client_data[ $clientkey ]);
@fclose($sock);
echo "A client disconnected. Now there are total ". count($client_socks) . " clients.\n";
continue;
}

//PARSE DATA AND PUT IN MYSQL

[W H A T E V E R   Y O U   L I K E]

//FILL DB
$query = "INSERT INTO db SET everything you want";
$sql = mysql_query($query) or die(mysql_error());
}
    }
}
?>

If you put some echos, please consider that I did in 2 ways:
  1. append to logfile
  2. echo out
Where you see that I echoed out, consider that this will echo in the shell and it's perfect for live debug as you start the socket. But in a production environment you would append to the logfile instead.

BASH SCRIPT /etc/init.d (copy e.g. crond and you will inherit the right permission, then write over this):

#!/bin/bash
#
# /etc/init.d/myDAEMON
#
# Starts myDAEMON
#
# chkconfig: 345 95 5
# description: Runs the myDAEMON daemon.
# processname: myDAEMON

# Source function library.
. /etc/init.d/functions

#startup values
log=/var/log/myDAEMON.log

#verify that the executable exists
test -x /var/www/html/myDAEMON.php || exit 0RETVAL=0

#
# Set prog, proc and bin variables.
#
prog="myDAEMON"
proc=/var/lock/subsys/myDAEMON
bin=/var/www/html/myDAEMON.php

start() {
# Check if myDAEMON is already running
if [ ! -f $proc ]; then
   echo -n $"Starting $prog: "
   daemon $bin --log=$log
   RETVAL=$?
   [ $RETVAL -eq 0 ] && touch $proc
   echo
fi

return $RETVAL
}

stop() {
echo -n $"Stopping $prog: "
killproc $bin
RETVAL=$?
[ $RETVAL -eq 0 ] && rm -f $proc
echo
        return $RETVAL
}

restart() {
stop
start
}

reload() {
restart
}

status_at() {
  status $bin
}

case "$1" in
start)
start
;;
stop)
stop
;;
reload|restart)
restart
;;
condrestart)
        if [ -f $proc ]; then
            restart
        fi
        ;;
status)
status_at
;;
*)

echo $"Usage: $0 {start|stop|restart|condrestart|status}"
exit 1
esac

exit $?
exit $RETVAL

Now you can type from the shell
service myDAEMON start
service myDAEMON stop
service myDAEMON restart
chkconfig --add myDAEMON

which means that you can start, stop, restart and start on launch, like you usually do for every other binary... ah, WHAT A FUN!!!!

venerdì 12 dicembre 2014

Considerations to pass Cisco conceptual exams

I write this post to remember myself some best practices based on my own experience when taking Cisco conceptual exams. These tipically are the question - single or multichoice answers and contain some common distractors which can be easily identified and avoided.

I also need to specify that I adopted the self-study approach for several reasons:

  • it's really cheap compared to a classroom (physical or virtual) course
  • I can fit my study slots to my other roles of father and employee
  • I can concentrate on some micro concepts which seem stupid but are still recurring (e.g. DSL and cable TV connections are asymmetric)
  • I can draw some schemes and memorize them as a picture, both while I draw them and when I study them later on

This approach perfectly fits to the considerations about Cisco questions I'd like to share with you, but it's mandatory that you deeply study the material: 818 minimum pass score means that you just can't skimmer the Cisco books unless it's your 20th Cisco exam in 5 years...

  • if an answer contains terms which you haven't heard before, it's wrong or you need to study a some more...
  • if an answer contains terms or concepts which you know from your real life experience, but you are sure that they aren't written in the Cisco book, it's wrong.
  • if you exercised a lot on Pearson exam simulations (questions come directly from Cisco) and you're sure that they're wrong, remember to memorize the wrong answer since Cisco wants that answer and assumes that Cisco is always right. If you don't trust me here's an example (summarized for copyright matters):
    • PC1 and PC2 are separated by several routes and communicate each other. What is the largest entity (it's specified IN SIZE) that make it from PC1 to PC2? Cisco says packet, I say frame (it's in the answers): in my knowledge a frame is packet + L2 encapsulation so it's LARGER IN SIZE than the packet just because it contains the packet itself!!!
    • Which command makes you exit from conf mode to exec mode? Well "end" and "exit", while "CTRL+Z" is considered an error although it's explicitly adviced as a return to EXEC in any online Cisco manual...
  • Sometimes the questions present complex scenarios, hiding a simple question and a simple answer: you can bypass the complexity reading the question (no matter if you don't understand everything) and then all the answers. The answers themselves will show you if you need to read again the question and really understand that scenario, or if it's simpler than it looks. Usually Cisco would display a graphic for really complex scenarios.

venerdì 5 dicembre 2014

[SNIPPET][bash] extract email addresses from CPanel

This requires that you activated the SSH access via CPanel.

Works well with hostmonster, bluehost and in general with any chrooted environment based on CPanel, where you don't have root password / sudo access. I haven't tried it on WHM based hosting providers.

#!/bin/bash
OWNER=$@
CONTACT=`ls -1A /var/cpanel/users/`

count=1
for x in `echo -n "$CONTACT"`;do
  users=`grep -i ^dns /var/cpanel/users/"$x" |cut -d= -f2`
  DOMAIN[$count]=$users
  count=$[$count+1]
  echo "Login:        `echo "$x"`"
  for i in `echo "${DOMAIN[@]}" | sed  's/ /\n/g'`;do
    for n in ` ls -A /home/"$x"/mail/"$i"/ 2&gt;/dev/null`;do
      if   [ "$n" == "cur" ];then echo "$n" &gt; /dev/null
      elif [ "$n" == "new" ];then echo "$n" &gt; /dev/null
      elif [ "$n" == "tmp" ];then echo "$n" &gt; /dev/null
      elif [ "$n" == "" ];then echo "$n" &gt; /dev/null
      else
        echo  "$n"@"$i"
      fi
      done
    done
  echo;
  echo;
done