Et c’est reparti, pour le mois de juillet cette fois !
Author Archives: Patrick
Quelques liens en vrac…
Une petite sélection de liens pour le mois de Juin:
Not doing Code Reviews? What’s your excuse?
Getting started with android development
What are your favorite Vim tricks?
GNU Screen: Working with the Scrollback Buffer
C++ clear winner in Google language tests
Scaling with MongoDB (or how Urban Airship abandoned it for PostgreSQL)
SECCURE Elliptic Curve Crypto Utility for Reliable Encryption
PHP, Reflection et Annotations
J’ai récemment eu à coder à mon taf des jeux de tests pour valider le comportement de mon serveur de pub, et pour ça, j’ai codé des petits scripts tout bête pour créer/modifier les données dans une base postgresql. J’aurai pu prendre un ORM tout fait (propel, doctrine…), mais j’ai juste codé mes classes tout bêtement et le plus simplement du monde. Un exemple:
{
public $id;
public $marque;
public $couleur;
};
Pour les requêtes d’insert, au lieu d’avoir des méthodes spécialisées dans chaque classe, j’ai codé juste une fonction save() qui prend l’objet à sauver, et qui parse grâce aux fonctions get_class_* ses propriétés pour générer les requêtes d’INSERT ou d’UPDATE. Cette fonction ressembait dans un premier temps à:
{
$class_name = get_class($obj);
$class_vars = get_class_vars($class_name);
$fields_names = '';
$fields_values = '';
$fields_count = 0;
foreach($class_vars as $var_name => $var_value)
{
if(isset($obj->$var_name))
{
$fields_names .= $var_name . ', ';
if(is_string($obj->$var_name))
$fields_values .= '\'' . $obj->$var_name . '\', ';
else
$fields_values .= $obj->$var_name . ', ';
$fields_count ++;
}
}
if(0 == $fields_count)
{
return -1;
}
// Remove trailing ', ';
$fields_names = substr($fields_names, 0, strlen($fields_names) - 2);
$fields_values = substr($fields_values, 0, strlen($fields_values) - 2);
return "INSERT INTO " . $class_name . "(" . $fields_names . ") VALUES (" . $fields_values . ");";
}
Ca me contentait dans pas mal de cas simples, mais très rapidement, cela à montré ses limites. J’avais besoin de connaître plus précisement les types des champs dans la base. Et la solution a été d’utiliser les annotations, pour ne pas avoir de longues et lourdes modifications à faire dans toutes mes classes d’objet. PHP ne propose pas out of the box un système complet d’annotation, mais incorpore les outils pour développer un comportement analogue: les classes Reflection.
Ces classes permettent de faire du « reverse engineering » sur les classes, les interfaces etc pour retrouver les différentes composantes de ces objets, qu’ils soient instanciés ou non. Et de plus, elles permettent de récupérer les commentaires des classes, des méthodes et des propriétés s’ils existent. Il n’y a qu’à les mettre en application:
/**
* Commentaire classe voiture
*/
class Voiture
{
/**
* Commentaire propriete id
*/
public $id;
}
$voiture = new Voiture;
$reflection = new ReflectionClass($voiture);
echo "Classe:\n";
echo $reflection->getDocComment() . "\n";
foreach($reflection->getProperties() as $property)
{
echo $property->name . ":\n";
echo $property->getDocComment() . "\n";
}
Ce qui donne:
/**
* Commentaire classe voiture
*/
id:
/**
* Commentaire propriete id
*/
$
Il ne resterait plus maintenant qu’à définir ses keywords, parser les commentaires propement pis à coder son propre mini-orm sans modifier les objets finaux.
Et voilà un début:
class DbObject
{
public function save()
{
$schema = $this->buildSchema($this);
/* Ici y a du code */
return TRUE;
}
private function buildSchema()
{
$class_name = get_class($this);
$class_vars = get_class_vars($class_name);
$class_reflection = new ReflectionClass($this);
echo $class_reflection->getDocComment();
$schema = array();
foreach($class_vars as $var_name => $var_value)
{
$prop_reflection = $class_reflection->getProperty($var_name);
$comment = $prop_reflection->getDocComment();
$comment = preg_replace(',\/\*\*(.*)\*\/,', '$1', $comment);
$comments = preg_split(',\n,', $comment);
$key = $val = NULL;
$schema[$var_name] = array();
foreach($comments as $comment_line)
{
if(preg_match(',@(.*?): (.*),i', $comment_line, $matches))
{
$key = $matches[1];
$val = $matches[2];
$schema[$var_name][trim($key)] = trim($val);
}
}
}
var_dump($schema);
return $schema;
}
};
class Voiture extends DbObject
{
/**-
* @DbType: integer
* @DbValue: 0
*/
public $id;
/** @DbType: string
*
* Blabla.
*
*/
public $marque;
/** @DbType: string */
public $couleur;
};
$voiture = new Voiture;
$voiture->id = 1;
$voiture->marque = 'renault';
$voiture->couleur = 'bleu';
$voiture->save();
buildSchema retournera dans cet exemple:
array(3) {
["id"]=>
array(2) {
["DbType"]=>
string(7) "integer"
["DbValue"]=>
string(1) "0"
}
["marque"]=>
array(1) {
["DbType"]=>
string(6) "string"
}
["couleur"]=>
array(1) {
["DbType"]=>
string(6) "string"
}
}
Pour finir, quelques petites références sur le sujet:
- http://www.php.net/manual/fr/book.reflection.php
- http://sebastian-bergmann.de/archives/488-Annotations-in-PHP.html
Et au final, au lieu de réinventer une roue bien vieille, je suis parti utiliser Addendum:
Controler les core dump
La génération des fichier core contiennent l’état de la mémoire d’un process, et a lieu généralement suite au crash de ce process. Sous Linux, le core dump est provoqué suite à la réception par le process de signaux spécifiques documentés dans le man 7 signal:
Signal Value Action Comment
----------------------------------------------------------------------
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating point exception
SIGSEGV 11 Core Invalid memory reference
SIGBUS 10,7,10 Core Bus error (bad memory access)
SIGSYS 12,31,12 Core Bad argument to routine (SVr4)
SIGTRAP 5 Core Trace/breakpoint trap
SIGXCPU 24,24,30 Core CPU time limit exceeded (4.2BSD)
SIGXFSZ 25,25,31 Core File size limit exceeded (4.2BSD)
Avant toute chose, il faut vérifier que le système est capable de générer ces fichiers. En effet, sous Ubuntu par exemple, la user limit (ulimit) pour la taille des fichiers core est nulle. Il faut donc permettre au système d’écrire des fichiers de taille illimité:
On peut trivialement vérifier que les fichiers core sont générés. On lance un shell, et on envoie un signal SIGSEGV qui va faire crasher ce nouveau process ($$) pour générer le core:
$ kill -SEGV $$
Segmentation fault (core dumped)
$ ls -l core
-rw------- 1 mycroft mycroft 3784704 2011-04-02 13:02 core
Cependant, parfois, on veut pouvoir controler un peu plus ce fichier, en particulier son nom et sa destination. Les noyaux Linux 2.6 permettent cela grace à la variable système kernel.core_pattern (ou /proc/sys/kernel/core_pattern) qui sert de template pour le nom de fichier et qui a comme valeur par défaut « core ». Ce template accepte un chemin absolu et les modificateurs de format suivant:
- %p – pid
- %u – uid
- %g – gid
- %s – numéro de signal
- %t – temps UNIX / timestamp
- %h – hostname
- %e – nom de fichier executable
- %% – « % »
On décide donc de placer nos fichiers core dans /tmp/core, et qu’ils aient le format « timestamp.nom-executable.pid.uid:gid ». Pour cela, on modifie /proc/sys/kernel/core_pattern avec le format de notre choix:
# echo "/tmp/core/%t.%e.%p.%u:%g" > /proc/sys/kernel/core_pattern
Et on teste:
$ bash
$ kill -SEGV $$
Segmentation fault (core dumped)
$ ls -l /tmp/core
total 3584
-rw------- 1 mycroft mycroft 3784704 2011-04-02 13:16 1301778979.bash.19366.1000:1000
Il ne reste plus qu’à le configurer de manière à toujours reproduire ce comportement, en particulier après un reboot dy système. Pour cela, il n’y a qu’à ajouter la ligne suivante dans le fichier /etc/sysctl.conf:
De plus, pour conserver la valeur de ulimit, il faudra modifier /etc/security/limits.conf et affecter la valeur « unlimited » à l’item « core »:
#<domain> <type> <item> <value> * soft core unlimited
A noter que cette modification prendra effet au prochain log-in et non à l’ouverture d’une nouvelle console dans X.
Se connecter proprement à ssh via un proxy web
Cet article est issu d’une précédente version de mon weblog, mais perdu dans l’oubli. Il remonte à Avril 2008. Je le remets ici à jour.
Un problème au taf, si ce n’est le prix du café au distributeur, est la non possibilité de se connecter sur sa box avec son ssh. Et bien, Dag Wiers propose une solution propre (qui n’oblige pas à installer son sshd sur le 443 et d’utiliser corkcrew), mais d’utiliser proxytunnel qui marche plutot bien.
Pour résumer la chose, cela implique d’avoir un serveur avec un apache où on a la main dessus, mod_proxy/mod_proxy_connect et mod_proxy_http d’installés, possiblement mod_ssl (mais ça crée un petit problème de taille à cause d’une non-feature d’apache2), et un peu de configuration.
N’ayant pas la main sur mon serveur au taf, c’est pas facile pour tester. J’ai donc aussi installé privoxy en local pour faire mes tests. Par contre, après utilisation, j’ai pas réussi à faire passer un CONNECT sur un port de destination 80, privoxy ne voulait pas, et le limit-connect semblait pas vouloir être changé. En même temps il était 5h du mat, j’ai peut être oublié un truc.
Alors, voilà la conf que j’ai faite sur ma debian (serveur ssh):
Server ssh sur le 22 et 2222;
Apache2 de base installé (bon, avec php mais là on s’en fiche);
On configure le bouzin:
remote# a2enmod proxy
remote# a2enmod proxy_connect
Il faut générer un certificat (.pem) pour notre nouveau serveur https. Pour çà, y a un super outil dans debian, et c’est make-ssl-cert:
remote# chmod a+r /etc/apache2/ssl/apache.pem
Et un virtualhost sur le 443, qui inclue directement les directives pour mod_ssl et mod_proxy:
SSLEngine On
SSLCertificateFile /etc/apache2/ssl/apache.pem
ServerAdmin mycroft@minithins.net
DocumentRoot /home/web/minithins.net
ServerName cns.minithins.net
HostnameLookups On
ProxyRequests on
AllowCONNECT 2222
ProxyVia on
<proxy *>
Order deny,allow
Deny from all
Allow from client.monkeyz.eu
</proxy>
</VirtualHost>
Un redémarrage d’apache, et on va tester avec stunnel:
Restarting web server: apache2 ... waiting .
...
client# stunnel -f -c -d 12345 -r spine.minithins.net:443
2011.04.02 12:22:22 LOG5[11844:140613013587712]: stunnel 4.29 on x86_64-pc-linux-gnu with OpenSSL 0.9.8o 01 Jun 2010
2011.04.02 12:22:22 LOG5[11844:140613013587712]: Threading:PTHREAD SSL:ENGINE Sockets:POLL,IPv6 Auth:LIBWRAP
2011.04.02 12:22:22 LOG5[11844:140613013587712]: 500 clients allowed
...
Sur une autre console:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
CONNECT spine.minithins.net:2222 HTTP/1.0
Connection closed by foreign host.
Hum. Ca ne fonctionne pas.
Un peu de recherche (ou de triche, car Dag Wieers donne la réponse), Apache, ou plus précisement le mod_proxy_connect est buggé. Mais un patch existe. Il faut appliquer un des patchs proposés (surement le dernier en date, dans mon exemple celui pour 2.2.16), et lancer un coup de rebuild des packages apache via les scripts debian:
remote# apt-get install devscripts fakeroot gcc bzip2 dpkg-dev
...
remote# apt-get build-dep apache2.2-common
...
remote# apt-get source apache2.2-common
...
remote# cd apache2-2.2.16/modules/proxy
remote# wget -O mod_proxy_connect.diff "https://issues.apache.org/bugzilla/attachment.cgi?id=26225"
remote# patch -p0 < mod_proxy_connect.diff
… et c’est parti pour tout rebuilder: …
remote# cd /tmp/apache2-2.2.16
... (long) ...
… et finalement on met à jour le package apache2.2-bin:
(Reading database ... 48179 files and directories currently installed.)
Preparing to replace apache2.2-bin 2.2.16-6+squeeze1 (using .../apache2.2-bin_2.2.16-6+squeeze1_amd64.deb) ...
Unpacking replacement apache2.2-bin ...
Setting up apache2.2-bin (2.2.16-6+squeeze1) ...
Processing triggers for man-db ...
remote # service apache2 restart
Restarting web server: apache2 ... waiting .
On reteste avec stunnel:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
CONNECT spine.minithins.net:2222 HTTP/1.0
HTTP/1.0 200 Connection Established
Proxy-agent: Apache/2.2.16 (Debian)
SSH-2.0-OpenSSH_5.5p1 Debian-6
Yay ! First step donne. Plus qu’à tester via un proxy http et utiliser le client ssh.
Pour tester, j’installe en local un privoxy:
On pourra modifier l’adresse d’écoute de privoxy (localhost:8118), et le modifier (par exemple, 127.0.0.1:8080 pour éviter qu’il prenne l’adresse ipv6 au lieu de la v4).
On crée un profil ssh en accord, dans ~/.ssh/config:
DynamicForward 1080
ServerAliveInterval 30
# proxy.dev pointe vers localhost car mon proxy est sur localhost.
ProxyCommand proxytunnel -v -X -p proxy.dev:8118 -r spine.minithins.net:443 -d %h:%p -H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n"
On notera dans mon exemple que j’utilise le flag -X. Il permet de préciser que j’utilise une connection sécurisée ssl entre les deux proxy (entre mon proxy et le mod_proxy de mon apache). Il est possible que le proxy que vous utiliserez ne l’aime pas. Dans ce cas, n’hésitez pas à joeur avec -X, -e et -E).
Et si on restestait:
...
Tunneling to spine-proxy:2222 (destination)
Communication with remote proxy:
-> CONNECT spine-proxy:2222 HTTP/1.0
-> Proxy-Connection: Keep-Alive
-> User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n
<- HTTP/1.1 502 Proxy Error
HTTP return code: 502 Proxy Error
...
What ?!
Dans les logs, on voit:
Il n’aime pas trop que j’utilise « spine-proxy » comme alias. Solution, retirer le -d %h:%p (et le remplacer par le vrai host de la machine), ou changer l’alias. Après correction:
OpenSSH_5.5p1 Debian-4ubuntu5, OpenSSL 0.9.8o 01 Jun 2010
...
debug1: Executing proxy command: exec proxytunnel -v -X -p proxy.dev:8118 -r spine.minithins.net:443 -d spine:2222 -H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\\n"
...
SSL local to remote proxy enabled
Local proxy proxy.dev resolves to 127.0.0.1
Connected to proxy.dev:8118 (local proxy)
Tunneling to spine.minithins.net:443 (remote proxy)
Communication with local proxy:
-> CONNECT spine.minithins.net:443 HTTP/1.0
-> Proxy-Connection: Keep-Alive
-> User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n
<- HTTP/1.0 200 Connection established
<- Proxy-Agent: Privoxy/3.0.16
Tunneling to spine:2222 (destination)
Communication with remote proxy:
-> CONNECT spine:2222 HTTP/1.0
-> Proxy-Connection: Keep-Alive
-> User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n
<- HTTP/1.0 200 Connection Established
<- Proxy-agent: Apache/2.2.16 (Debian)
Tunnel established.
debug1: Remote protocol version 2.0, remote software version OpenSSH_5.5p1 Debian-6
...
remote#
Et ça marche ! Rock’n'Roll ! :°)