Monthly Archive for août, 2007

Men Are Ants: New Style!

Bonjour,

Men Are Ants n'est pas mort. En effet, malgré que ça fait trois mois que j'ai rien foutu dessus, de nouveaux graphistes ont apparus et commencent à travailler sur un remodelage complet. Ceci implique de modifier le jeu pour supporter la 3D isométrique !

Je suis pour le moment très occupé par INL d'un côté, et Web Rhap de l'autre, alors je n'ai pas encore le temps de commencer. Heureusement, trois personnes admirables, lodesi, Zura et Edb, m'ont proposé de l'aide. Bon je sais que les deux premiers risquent de pas faire grand chose car c'est plutôt moi qui les pousse à contribuer, mais le troisième a l'air motivé.

Concernant les graphistes, on trouve Sébastien Matti, chef graphiste, skeespin et Luc Léger, graphistes, et lululaglue, qui lui se charge de refaire l'interface du jeu.

Sébastien Matti a déjà fait un super essai que je vais dévoiler sans plus attendre :

Je trouve ça personnellement très beau et très satisfaisant.

Comme je sais que vous portez un intérêt sans faille à l'évolution de la situation, vous pouvez lire le thread de la mailing list du jeu concernant cet essai ici.

OpenBSD, Ep2

Suite de l'épisode 1.

            _.-|-/\\-._
         \\-'          '-.
        /    /\\    /\\    \\/
      \\/  <    .  >  ./.  \\/
  _   /  <         > /___\\ |.
.< \\ /  <     /\\    > ( #) |#)
  | |    <       /\\   -.   __\\
   \\   <  <   \V      > )./_._(\\
  .)/\\   <  <  .-     /  \\_'_) )-..
      \\  <   ./  /  > >       /._./
      /\\   <  '-' >    >    /
        '-._ < v    >   _.-'
          / '-.______.-' \\
                 \\/

Voilà donc comme j'ai rien à faire ce soir (quoi lodesi je dois coder la 3D iso sur MenAreAnts ?), je vous raconte rapidement ce que j'ai fais dimanche concernant la suite de mon serveur OpenBSD.

Ainsi que vous avez pu le lire ici, j'ai passé la machine comme serveur au lieu de routeur, ne faisant utiliser plus qu'une seule carte ethernet. Comme j'ai desactivé la carte son dans le BIOS, je pense que je n'aurais de toute façon pas eu de problème, mais je préfère la nouvelle topologie, deux routeurs à la suite n'étaient pas forcément très utiles.

L'installation a été bien sur très rapide, en un quart d'heure c'est bouclé et on a un système bootable. Peu de choses à rajouter après.

Je me suis tout d'abord empressé d'installer un httpd, le fork de l'équipe OpenBSD d'Apache 1. En effet, voulant naïvement installer à l'origine Apache 2, je reçois un avertissement d'une vingtaine de ligne m'expliquant que « Apache 2 sux ».
Tout d'abord il faut savoir qu'il est chrooté dans /var/www, ce qui explique pourquoi je n'ai pas trouvé la conf tout de suite (/var/www/conf).

Bien sur très facile à configurer, php5 facile à installer, rien à dire, si ce n'est que pour que chaque utilisateur ait un compte, il faut créer un répertoire dans /var/www/users/ (oui, chrooté il ne peut accéder à /home).

Ce que j'ai fais, car j'ai restauré les données des différents comptes /home que j'avais sauvegardé, c'est déjà de déplacer /var/www dans /home/www, de lier /var/www à ça, car je dois avouer que sur le faible disque de 10Go, je n'avais guère envie de faire plus de deux partitions.
J'ai donc lié chaque $home$/public_html à /home/www/users/$home$ avec un accès en écriture sur celui-ci.

Continuons avec mon installation d'OpenBSD, je voulais installer MRTG. Avant d'entrer dans les détails, je vais vous dire ce que je pense de pkg_add...

À mon avis, c'est l'ancètre d'apt de Debian. C'est relativement mal foutu, à chaque appel à pkg_add, il va récupérer sur le serveur ftp la liste entière des packets (troll: heureusement qu'il y en a peu), ce qui équivaut à un « apt-get update && apt-get install », le cache en moins. Ça ralenti pas mal l'opération. La gestion des dépendances est bonne, mais souffre du problème que je vais vous citer ci-dessous.

À l'installation de MRTG, j'obtiens ceci :

 root@server.rdl.fr ~ # pkg_add mrtg
Can't install gd-2.0.34p1: lib not found fontconfig.3.0
Even by looking in the dependency tree:
        jpeg-6bp3, libiconv-1.9.2p3, png-1.2.14p1
Maybe it's in a dependent package, but not tagged with @lib ?
(check with pkg_info -K -L)
If you are still running 3.6 packages, update them.
Can't install gd-2.0.34p1: lib not found freetype.13.1
Can't install mrtg-2.12.2p3: can't resolve gd-2.0.34p1
 root@server.rdl.fr ~ #

Donc on peut suspecter une dépendance foireuse. J'envoie donc très gentillement (j'ai pas dis que j'étais homosexuel) un mail à la mailing list d'OpenBSD concernant ce problème, en détaillant bien, donnant les caractéristiques de mon installation etc.
Je reçois à peine quelques minutes plus tard une réponse. Très satisfait, je vais lire. Je fus très déçu de constater que la réponse n'était que mon message d'origine, suivit de « Install the X11 libraries. ».
Je dois avouer être sceptique. Je ne sais pas si gd a vraiment besoin de la lib X11, mais dans ce cas là pourquoi ne pas installer automatiquement la dépendance ? Surtout que ce message d'erreur est tout sauf explicite.

Bref, continuons notre avancée dans le monde magique d'OpenBSD.

Je décide par la suite de tester la compilation de MenAreAnts Server sous cet OS, afin de corriger les éventuelles erreurs de compilation. J'ai été au final très satisfait de découvrir que juste deux ou trois headers en plus à inclure ont suffit.
Le serveur est lancé, avec un très beau MOTD comportant le dessin que vous pouvez trouver en haut de ce billet, et marche à merveille. J'ai d'ailleurs [encore] explosé pankkake dessus.

Bon comme je me rends compte que ce billet devient long, je vais donner une petite anecdote cocasse aux courageux qui sont allé jusque là.

En lisant la doc officielle d'OpenBSD, je vois un paragraphe qui explique la mise en place des quotas. N'ayant jamais essayé (ni même sous Linux), je me mets en tête de faire le test. Je tag donc la partition /home pour l'utilisation de quotas. J'utilise une commande histoire de paramétrer les quotas pour un utilisateur. Ayant l'âme d'un grand fou, je me dis que ça pourrait être marrant d'éditer le fichier /home/quotas.user.

Je tombe sur un fichier binaire, déçu, je quitte.... avec la commande vim :wq...
Du coup le fichier est réécrit, et bien sur pas identique.

Les conséquences ? Bah toute opération qui touchait de près ou de loin à /home bloquaient...
Donc quotaoff marchait pas, rm /home/quota.user marchait pas, umount /home marchait pas, reboot marchait pas.

J'ai donc été au final obligé de rebooter à la main. J'ai bien évidemment enlevé le flag userquota dans /etc/fstab... Je me dis qu'il était bien heureux de ne pas avoir testé les quotas sur / !

Voilà donc en conclusion de mon expérience OpenBSD, je dois dire que je suis plutôt satisfait, malgré mes quelques petits problèmes, je vais le garder sur le serveur.

Notez qu'à l'origine vous n'avez que gcc-3.3 et vi qui sont quand même pas très récents, mais bon chez OpenBSD on préfère les trucs vieux :)

Topologie

Afin d'illustrer mon dernier billet, je m'étais ce matin mis en tête de dessiner l'ancienne et la nouvelle topologie du réseau. Ça n'a absolument aucun intérêt, mais c'est assez marrant à faire, et ça m'a permis de découvrir quelques outils.

lanmap

Tout d'abord en cherchant dans les paquets Debian, je trouve lanmap qui est pas trop mal. En fait il détecte tout seul et génère un graphe fréquemment avec les statistiques de traffic.
Malheureusement, il s'est loupé pour moi, ça ne ressemble pas trop à ça :

lanmap.png

Les machines 192.168.0.87 et 192.168.0.56 ne sont pas placées ainsi, les trois desktops étant derrière un hub (ce que lanmap ne peut détecter, étant de niveau 2).

Cheops

Après quelques recherches sur Google, je trouve un utilitaire intitulé Cheops qui permet de créer le graphe.

En fait le projet Cheops a été abandonné il y a quelques années, puis repris pour être renommé Cheops-NG (pour New Generation).

J'ai voulu tester les deux qui en fait détectent le réseau tout seuls, mais comme lanmap, se goure complètement. Cette fois ils indiquent ma machine comme était connectée d'un côté au routeur (192.168.0.1), et de l'autre côté aux deux autres desktops (.87 et .56). En plus de ça on ne peut pas décider de ses liens, et on ne peut pas introduire de materiel de niveau inférieur à 3 dans les graphes.

Bref je laisse tomber.

Dia

Je me penche vers Dia qui au final, malgré qu'il ne soit pas spécialisé dans la topologie réseau, possède des images Cisco et permet de faire des schemas simplement.

Je peux donc maintenant vous présenter mon ancien réseau :

lastlan.png

Et mon nouveau réseau :

newlan.png

Le seul changement, c'est le routeur 192.168.1.10/192.168.0.1 qui passe en tant que simple serveur. Le réseau 192.168.1.0/24 est supprimé, et le routeur 192.168.1.1 prends l'ip 192.168.0.1 pour que le changement soit transparent sur les autres machines.

Ce qui va me permettre de retenter l'installation d'OpenBSD sur la vieille machine, n'ayant besoin que d'une carte réseau maintenant.

OpenBSD, Ep.1

Aujourd'hui j'ai voulu installer OpenBSD sur mon routeur, une vieille machine sur laquelle se trouvait une Debian.

Le but était de rafraîchir un peu la machine (car j'ai fais plein de conneries dessus), mais également de tester un nouvel OS.

Je précise que j'avais déjà installé une OpenBSD dans un qemu, donc je n'ai pas débarqué et je connaissais un peu le processus d'installation qui, au final, n'est guère plus compliqué qu'une Debian (ncurse en moins), une fois que l'on a compris le partitionnement particulier d'OpenBSD.

J'ai donc sauvegardé les données sensibles, téléchargé puis gravé l'iso de 5 Mo, et rebooté le routeur sur le CD.

Je débute l'installation, fait mon partionnement très basique. La machine est très vielle, et ne possède que 10 Go d'espace disque. Je décide de faire un / de 4.5Go, une swap de 256Mo, et le reste en /home.
Là où je commence à être surpris, c'est concernant la configuration du réseau. Il me détecte trois interfaces au lieu de deux, ne3, ep0 et ep1. Je me dis que ep0 et ep1 doivent correspondre à eth0 et eth1. Bon je me goure complètement, donc je retente avec la configuration de ne3 et ep1. Là j'ai ne3 qui marche, mais pas ep1.

Bref, je vais épargner les détails, en gros je me rends compte au final, déjà que le nom de l'interface corresponds au type de carte, ensuite que ep0 et ep1 ont la même adresse MAC, et que ne3 est l'interface 192.168.1.0/24 (sur lequel se trouve le routeur club-internet), et ep* l'interface du LAN 192.168.0.0/24. Oui je ne sais toujours pas ce à quoi corresponds ep*.

Post-installation, j'accède bien à Internet, il ne connait plus qu'ep0 et plus ep1, mais alors que ep0 est bien configurée, je n'arrive qu'à me pinguer et pas à pinguer les autres machines du LAN.
Je commence à chercher un peu, beaucoup, dans la doc dans ce que je vois de similaire à Linux, etc, je ne comprends pas.

Au bout d'une heure, las, je me décide à aller sur #openbsd @ irc.freenode.org afin de demander de l'aide. On me reçoit correctement, et après avoir exposé mon problème, on me demande la sortie de dmesg.
Après analyse, on me dit qu'il y a un conflit au niveau de l'IRQ de la carte réseau avec la carte son, qu'une solution est de prendre une nouvelle carte PCI, ou de booter sur un live cd afin de changer l'IRQ manuellement. Ne voulant pas acheter une nouvelle carte réseau, ne sachant pas comment changer l'IRQ (car nul point de vue matériel), je me rabats sur la solution de virer la carte son.

Malheureusement, après avoir ouvert mon PC, je me rends compte que celle-ci est incorporée à la carte mère. Pas de chance.

Après reboot, l'interface ne3 ne fonctionne pas plus. Je commence à desespérer un peu, d'autant plus que sur IRC ils se foutent de moi lorsque je leur informe que Linux gérait le problème, que ce dernier réglait apparament tout seul les conflits d'IRQ.

Dépité, je décide finalement de télécharger une Debian Etch afin de la réinstaller sur le routeur. Cependant, je fut surpris de voir que celle-ci ne détecte qu'une seule carte réseau (ep0), et qu'en plus elle n'arrive pas à la faire marcher.

Bref, au final, ceci n'est pas une expérience très significative, vu que le problème vient bien de la machine.

Ma solution a été de changer la topologie du réseau, afin de brancher le hub du LAN 192.168.0.0/24 au routeur club-internet, et de changer le réseau de ce dernier (il était en 192.168.1.0/24).

Je pense que je vais du coup récupérer le routeur pour en faire un serveur. Le mieux serait que j'installe OpenBSD, je n'aurai donc besoin que d'une seule interface.

Par contre, je ne rebooterai pas histoire d'éviter le problème que j'ai connu (ne3 qui foire après un reboot).

Comme d'habitude, celui qui est arrivé jusqu'ici a le droit à toute mon indifférence.

La magie du C++

Il m'arrive de déconsidérer le C++ par certains aspects laids (qui ont pour but de garder une compatibilité avec le C), mais en l'occurence je suis satisfait d'avoir fait un petit système très joli (note: je ne parle pas d'Anicée) grâce à la magie du C++.

Je suis actuellement sur WebRhap v2, un logiciel propriétaire à vocation commerciale pour la société RDL.

J'avais écris à l'origine une première version avec des outils pour Windows, mais après quelques considérations il me devenait inévitable d'écrire une version portable. C'est donc ce que je fais, non sans peine, car je dois le faire rapidement, et bien que je peux réutiliser les morceaux de code de haut niveau (basées sur des classes que je peux réécrire sans en changer l'interface), je m'aperçois d'erreurs de conception, ou j'ai l'idée d'écrire des trucs plus élaborés.

C'est ainsi que j'en suis venu à penser à un mega système de logs de la mort qui tue, qui n'a rien de révolutionnaire en soit, mais qui est, je trouve, très élégant à l'utilisation.

Ma problématique se base sur le fait que je voulais utiliser les flux, car j'en ai marre de passer par des valist, ce qui m'oblige, lors du passage de variables de type std::string, à appeler l'attribut c_str() renvoyant le char*, ce qui rends très laid.

J'ai donc commencé à me pencher sur les flux, mais voulant utiliser des flags pour décrire tel ou tel niveau de gravité (sachant qu'on pourrait combiner des flags pour avoir plusieurs comportements simultanément), il me fallait trouver un moyen de donner un argument au flux.

J'ai donc au final opté pour un mega système très joli mais qui n'apporte absolument rien de plus que le traditionnel Debug(int flags, const char* pattern, ...);, mais qui m'excite particulièrement.

TDebug

J'ai d'abord une classe TDebug qui a pour unique but d'être instanciée en tant que Debug au lancement du programme comme variable globale.

 
class TDebug
{
/* Constructeur */
public:
 
	TDebug();
 
	~TDebug();
 
/* Attributs */
public:
 
	void SetLoggedFlags(std::string s);
	unsigned int LoggedFlags() const { return logged_flags; }
 
/* Private */
private:
 
	unsigned int logged_flags;
};
 

Rien de spécial. La variable logged_flags sert à garder les flags qu'on logue (paramétrable dans la conf). On constate que SetLoggedFlags(std::string s) récupère une chaîne. En effet, cette fonction parse directement ce qu'on lui donne comme labels dans la conf, et met les flags par lui même.
Mais on ne va pas s'y intéresser ici, d'autant plus que ça n'est pas intéressant.

TDebug::flux

L'idée est qu'une sous structure va servir de flux, et on l'obtient par l'opérateur [].

Voici le code tout ce qu'il y a de plus con (dans TDebug) :

 
	flux operator[](size_t __n)
	{
		return flux(__n);
	}
 

Lorsque l'on appelle Debug[i], on obtient un objet de type TDebug::flux qui va capturer lui même l'opérateur << :

 
	class flux
	{
		std::string str;
		size_t flag;
 
	public:
		flux(size_t i)
			: flag(i)
		{}
 
		~flux();
 
		template<typename T>
		flux& operator<< (T s)
		{
			std::ostringstream oss;
			oss << s;
			str += oss.str();
			return *this;
		}
	};
 

Ceci mérite quelques explications.

Tout d'abord, on a dans un std::string une chaîne, et dans un size_t les flags correspondants. Jusque là ça va.

Le constructeur stocke le flag passé, et le destructeur est implémenté dans Debug.cpp et on en parlera plus tard.

Intéressons-nous à operator<<. J'ai utilisé un template dans le but de récupérer n'importe quel type T, de le passer à un std::ostringstream afin de profiter de sa capacité de conversion et de récupérer la chaîne via l'attribut str().

Notez qu'on renvoie une référence de nous même. Ce qu'il faut préciser, c'est que dans une instruction de ce genre :

 
   std::cout << "blah " << i << " bla bla" << std::endl;
 

On évalue d'abord std::cout << "blah ", qui renvoie un objet de type std::ostream, afin d'évaluer ensuite std::ostream << i, et ainsi de suite. La rencontre de std::endl; ajoute une fin de ligne et fait un flush (il existe d'ailleurs std::flush).

Ici, on ajoute à str à chaque fois la chaîne qu'on récupère. Et ainsi que vous l'aurez compris, à la fin de l'exécution de l'instruction, notre TDebug::flux est détruit, et c'est là qu'on fait notre flush.

Syslog

Ainsi que j'ai omis de le préciser au début, probablement du fait que je reflechis au fur et à mesure que j'écris et que j'ai la flemme de corriger le début, je me sers de syslog pour logger.

Vous vous doutez donc maintenant de l'implémentation du constructeur TDebug::TDebug et du destructeur TDebug::~TDebug :

 
TDebug::TDebug()
	: logged_flags(DEFAULT_LOGGED_FLAGS)
{
	openlog("WebRhap", LOG_CONS, LOG_DAEMON);
}
 
TDebug::~TDebug()
{
	closelog();
}
 

Le destructeur de TDebug::flux sera pas beaucoup plus compliqué :

 
static struct
{
	int flag;
	int level;
	const char* s;
} all_flags[] =
{
	{ W_DEBUG,      LOG_DEBUG,   "DEBUG"      },
	{ W_PARSE,      LOG_DEBUG,   "PARSE"      },
	{ W_CMDS,       LOG_INFO,    "CMDS"       },
	{ W_WARNING,    LOG_WARNING, "WARNING"    },
	{ W_BIGWARNING, LOG_WARNING, "BIGWARNING" },
	{ W_ERR,        LOG_ERR,     "ERR"        },
	{ W_CONNEC,     LOG_NOTICE,  "CONNEC"     },
	{ W_MODIF,      LOG_NOTICE,  "MODIF"      },
	{ W_WEBSITE,    LOG_NOTICE,  "WEBSITE"    }
};
 
TDebug::flux::~flux()
{
	int i;
 
	if(!(flag & Debug.LoggedFlags()))
		return;
 
	for(i = (sizeof all_flags / sizeof *all_flags) - 1; i >= 0 && !(flag & all_flags[i].flag); --i);
 
	if(i < 0)
		syslog(LOG_WARNING, "[SYSLOG] (%X) Unable to find how to log this message: %s", flag, str.c_str());
	else
	{
		syslog(all_flags[i].level, "[%s] %s", all_flags[i].s, str.c_str());
		#ifdef DEBUG
			std::cout << "[" << all_flags[i].s << "] " << str << std::endl;
		#endif
	}
}
 

Tout d'abord on vérifie qu'on logue bien ce flag, ensuite on cherche dans le tableau de structure all_flags. Si ça n'est pas trouvé, on met un message pour dire que c'est la mémerde, et sinon on logue en prenant le level spécié, le label, et le message.

Spécialisation de templates

C'est là que j'en ai un minimum chié, et non pas parce que c'est compliqué, mais tout simplement du fait que G++ est un peu bizare sur quelque chose apparament pas spécifié dans les normes C++ (à confirmer).

Tout d'abord, je souhaitais spécialiser std::string pour éviter qu'il passe inutilement par un flux si il lui suffisait d'utiliser l'opérateur += de str.

Le code est trivial :

 
		template<>
		flux& operator<< <std::string> (std::string s)
		{
			str += s;
			return *this;
		}
 

Si je mets ce code dans TDebug::flux juste après la définition du template operator<<(T s), g++ m'envoie chier car il ne souhaite pas que je définisse la spécialisation dans la classe directement mais en dehors.

Je définis donc en dessous de la définition de TDebug (en rajoutant les namespaces comme il faut), et il m'envoie chier lors de l'édition des liens car il a plusieurs références à la même fonction (même signature). C'est naturel, mais du coup je me sens emmerdé.
La solution a été tout simplement de mettre la fonction en inline, comme suit :

 
template<>
inline TDebug::flux& TDebug::flux::operator<< <std::string> (std::string s)
{
	str += s;
	return *this;
}
 

Du coup ça marche, et le code étant on ne peut plus faible, le passage en ligne de la fonction n'est pas un problème.
Ce qui est étrange c'est qu'il semblerait que G++ soit le seul compilateur à empecher de définir une spécialisation au sein même de la définition de la classe, ce qui me semble étrange, car je n'en vois pas trop l'intérêt.

Rajouté la spécialisation pour const char* qui suit le même principe, je ne vais pas le recoller tout de même.

Par contre là où ça devient très intéressant, c'est une spécialisation sur des objets spécifiques pour les mettre dans les logs sans trop d'efforts.
Voici l'exemple pour la classe Client :

 
template<>
inline TDebug::flux& TDebug::flux::operator<< <Client*> (Client* cl)
{
	str += "(";
	str += odbc2str(cl->Ip());
	str += ") ";
	return *this;
}
 

Passons sur l'appel à odbc2str(), car une contrainte est que WebRhap puisse supporter l'unicode, et donc passer par des chaines de caractères étendues (wchar_t*), ce qui m'oblige pour des appels à des fonctions externes n'ayant pas d'équivalentes pour les wchar_t*, à faire des conversions. odbc pour préciser que c'est une chaine de "type" ODBCXX_STRING (ce qui est juste un alias vers std::string ou std::wstring en fonction de la définition ou non de ODBCXX_UNICODE).

Fin

Tout ça pour arriver à un résultat relativement élégant :

 
/* Par exemple */
Debug[W_PARSE] << "S" << cl << "- " << buf;
 
/* Ou */
Debug[W_BIGWARNING] << "-" << fd << "- This fd is already used ! (connexion from " << ip << ")";
 

Ce qui va afficher dans daemon.log par exemple :

Aug  4 17:47:26 laptop WebRhap: [PARSE] S(127.0.0.1) - HEL WRW 4 kikoo lol^W
Aug  4 17:47:26 laptop WebRhap: [BIGWARNING] -4- This fd is already used ! (connexion from 127.0.0.1)

Bon maintenant que je suis arrivé à ce résultat, il me serait plaisant d'écrire dans un unique fichier webrhap.log plutôt que de squatter daemon.log, syslog.log et debug.log... Mais j'ai pas encore eu le temps de m'y pencher, car bon c'est cool de faire un joli système de logs, mais c'est pas la fonction principale du programme, et je suis encore loins du but !

P.S.: Dédicasse spéciale à toi, petit con, qui a lu ce post jusqu'au bout.




Bear