standing_daemon.jpg Nous allons voir ici comment faire pour qu'un script PHP puisse devenir un "Daemon" indépendant du navigateur web , personnellement je l'utilise par exemple dans un de mes projets en tant que partie serveur , le script et lancé au démarrage du serveur , puis écoute sur un port donné , pour effectuer une tache précise à la connexion d'un client ... A vous de trouver l'utilisation adéquate d'un tel système ..

Pré-requis :

Pour créer un daemon , nous allons devoir contrôler les processus via la bibliothèque PCNTL de php , pour l'installation , il faut juste compiler PHP avec l'option --enable-pcntl , et surtout , ne pas mettre --disable-cli ni --disable-cgi , car le script qui utilise les fonctions PCNTL , doivent être lancé en CGI , ou CLI , comme expliqué dans la documentation , il vous sera donc impossible de lancer votre script à partir d'un navigateur , sauf si vous utiliser php en mode CGI/FastCGI et non , en module apache .

Le principe :

Le principe est le suivant , l'utilisateur avec pouvoir ( ici root ) lance le script principale , qui dans un premier temps "fork" le processus courant , ( il crée donc un fils identique à lui-même ) , puis le processus pére se tue "Kill" . Le fils passe en chef d'orchestre change son processus en chef de session Le fils va ensuite changer d'utilisateur afin de s'exécuter sur un utilisateur avec le minimum de droits possible ( ici ce sera nobody ) . Dans un troisième temps , le fils va déclarer ses propres méthodes à la réception d'un signal du système . Et enfin , le script peut se lancer sans problème .., indépendamment du processus pére ou du terminal qui à lancer le script ..

L'exemple :

Comme expliqué plus haut , nous allons reprendre chaque étape importante et voir le code "PHP" correspondant ... aller c'est parti ...

Forker le processus pére :

$pid = pcntl_fork();
	   
if ($pid == -1)
    {
	/* Échec du fork*/
	echo "Échec du fork!\n";
	exit();
    }elseif ($pid)
    {
	/* Termine le process père */
	exit();
    }else
    {
	/* Process Fils*/

        //Fait du processus courant un chef de session
	posix_setsid();

        //Changement du répertoire courant pour l'exécution du script
	chdir('/');

        //change le umask de PHP en 0777
	umask(0);

        //Retourne l'identifiant du processus fils
	return posix_getpid();
    }

Voilà comment creer un démon .. dans le script ci-dessus , c'est la fonction pcntl_fork() qui s'occupe de dupliquer les processus , ensuite , vous imaginez deux processus qui s'exécutent en parallèle .. l'un qui a un $pid ( le pére , car c'est la pére qui a lancé le fork ) et un autre qui n'en a pas ( le fils , car $pid n'est pas défini ) . En tue donc le pére avec la commande exit() . Le processus fils continue donc seul son chemin .. il fait du processus courant un chef de session ... via la fonction posix_setsid(); , pour ensuite changer le répertoire courant , et le mask de php , pour au finale ,retourner son identifiant .

Changer d'utilisateur

Pour des raison de sécurité , il faut changer d'utilisateur courant , car là nous somme toujours en root :

if( !posix_setgid( 65534 ) )
    {
        print "Erreur  setgid :" . $gid . "!\n";
        exit;
    }

    if( !posix_setuid( 65534 ) )
    {
        print "Erreur setuid :" . $uid . "!\n";
        exit;
    }

Voilà , pour le changement d'utilisateur , nous avons récupéré l'UID et le GID de l'utilisateur nobody pour l'attribuer au processus fils via les fonctions posix_setuid() et posix_setgid()

Les signaux :

Le script fils peut déclarer ses propres méthodes pour gérer comme il l'entend ( ou plutôt comme VOUS l'entendez ) , la réaction aux signaux systéme .

declare(ticks = 1);

/* handle signals */
pcntl_signal(SIGTERM, 'sig_handler');
pcntl_signal(SIGINT, 'sig_handler');
pcntl_signal(SIGCHLD, 'sig_handler');

/**
* Signal handler
*/
function sig_handler($sig)
{
    switch($sig)
    {
        case SIGTERM:
        case SIGINT:
            exit();
        break;

        case SIGCHLD:
            pcntl_waitpid(-1, $status);
        break;
    }
}

La gestion des signaux ci-dessus , utilise les ticks déprécié depuis php 5.3 ... il faudra donc trouver une autre alternative ... ici , la gestion des signaux n'est pas obligatoire !

Lancement :

Pour lancer votre daemon, plusieurs solutions , soit apache fonctionne avec php en mode CGI/FastCGI , alors , vous pouvez lancer le daemon via votre navigateur ( mais mauvaise idée , car de ce fait , votre navigateur doit se trouver sur la même machine que votre script et être lancer en root ), sinon, ce sera en ligne de commande , comme ceci :

En root:

php /dir/de/votre/script/serveur.php

Le script se lance puis s'arrête aussitôt .. car en faite , le script que vous lancez est le process pére .. qui dés qu'il a créé son fils , se tue !

Pour vérifier que votre daemon se trouve bien dans la liste des processus , il vous suffit de le rechercher dans les process courant , exemple pour serveur.php :

ps -xa | grep serveur.php

Qui retourne une ligne de ce style :

25951 ? Ss 0:00 php /dir/du/script/serveur.php

Voilà , en espérant avoir écrit un script intéressant , sans trop entrer dans les détails ..

Christophe.