jul is a nolife

Wednesday, February 4 2009

Plugin cybermut pour rails

Je finis par publier ce plugin cybermut que j'ai sous le coude depuis un moment, et que j'ai enfin utilisé en production

script/plugin install http://svn.immateriel.fr/cybermut/trunk

Le README:

Basé sur le gem paypal et sur le code de Yann KLIS

Dans environment.rb:

Cybermut::Confirmation.action_url = "https://ssl.paiement.cic-banques.fr/test/paiement.cgi"
Cybermut::Confirmation.hmac_sha1_key = "SHA1_KEY"
Cybermut::Confirmation.hmac_sha1_pass = "SHA1_PASS"

Dans la vue:

cybermut_setup(no_commande, montant, tpe, options)

Exemple:

<%=  cybermut_form_tag %>
<%= cybermut_setup("order10", 100, "123456",'societe'=>"nomsociete", 'url_retour_ok'=>"http://site/paiement_ok", 'url_retour_err'=>"http://site/paiement_erreur") %>
<%= submit_tag "Payer maintenant"%>
</form>

La validation:

def validate_cybermut_order
  if request.raw_post
    confirmation=Cybermut::Confirmation.new(request.raw_post)
    # verification que la commande existe dans la base
    if Order.exists?(confirmation.reference)
      @order=Order.find(confirmation.reference)
      # verification de la validité du message du serveur cybermut
      if confirmation.acknowledge
        # verification du total
        if @order.total == confirmation.montant_euros
          @order.validation
          logger.info "CYBERMUT: order #{@order.id} is OK"
        else
          logger.info "CYBERMUT: order #{@order.id} FAILS with data problem"
        end
      else
        logger.info "CYBERMUT: order #{@order.id} FAILS with validation
      end
    else
      logger.info "CYBERMUT: order #{confirmation.reference} FAILS not found"
    end
  end
  render :text=>confirmation.response
end

Limitations : Pas de support d'une autre monnaie que l'euro

Wednesday, June 18 2008

Passer de rails 2.0 à rails 2.1

En voulant migrer l'un de mes projets Ruby on Rails de la version 2.0 à la version 2.1, j'ai découvert qu'en fait il n'y avait pas une totale rétro compatibilité. Une fois de plus, c'est assez désagréable de devoir mettre à jour son application vers une nouvelle version ... Voici quelques notes que j'ai trouvé à droite à gauche afin de faciliter la tâche.

Tout d'abord, config.action_view.cache_template_extensions dans config/environments/development a été purement et simplement éjecté. Jusque là tout va bien, il y a juste à supprimer la ligne.

Ensuite, il semble que le ''eager loading'' ai changé, ainsi lorsque l'on faisait

Table1.find(:all, :include=>"table2",:conditions=>["table3_id=?",1])

table2 ayant une colonne table3_id Pas de problème, un left join était généré. Dans rails 2.1, une erreur

Mysql::Error: Unknown column 'table3_id' in 'where clause': SELECT * FROM `table1`     WHERE (table2_id=1)

Je ne suis pas sur de comprendre quel est l'utilité du include à présent, il n'empêche qu'il faut à présent :

Table1.find(:all, :joins=>"table2",:conditions=>["table3_id=?",1])

pour obtenir le même résultat.

Enfin, pas mal de plugins plantent lamentablement, par exemple Globalize ou le plugin de thème. Il semble que pour les deux ce soit un problème avec les fonctions de template :

ActionView::Base::register_template_handle

étant remplacé par

ActionView::Template.register_template_handler

et

pick_template_extension

étant instancié via @finder

@finder.pick_template_extension

J'ai trouvé deux patch pour régler ça. Pour le plugin de thème c'est ici, et pour Globalize, . Problème pour Globalize, j'avais une erreur "can't dup FalseClass". Je n'ai pas cherché à comprendre et j'ai hacké vite fait bien fait le fichier vendor/plugins/globalize/lib/globalize/rails/action_view.rb avant la ligne

pn = Pathname.new(template_file_name)

J'ai juste ajouté :

if !template_file_name
   template_file_name=""
end

Je suis sur que pleins d'autres trucs peuvent encore poser problème, mais pour l'instant mon application semble fonctionner.

Thursday, April 3 2008

Add an id to your has_and_belongs_to_many join table

If you want to define a model for your has_and_belongs_to_many join table, you really need to care, because if your table has an id primary key, then the join will not work.

Imagine that you have two tables Product and Keyword and you want to link these with a product_keywords You will create a migration :


 create_table :product_keywords do |t|
      t.column :product_id, :integer
      t.column :keyword_id, :integer
    end

You need an id because you want easily access the table throught a model


class ProductKeyword < ActiveRecord::Base
  belongs_to :product
  belongs_to :keyword
  # something like voting for keyword in product
  has_many :votes
end

Then if you want to access the keywords with Product model


class Product < ActiveRecord::Base
 has_and_belongs_to_many :keywords, 
:class_name=>"Keyword", 
:join_table=>"product_keywords", 
:foreign_key=>"product_id", 
:association_foreign_key=>"keyword_id"
end

This actually doesn't work! Why? When the SQL statements will ask for the keywords columns, it will get all columns, including product_keywords. Since we have an id for keyword AND for product_keywords, the id results are not clear. We need to force the keywords column only.


class Product < ActiveRecord::Base
 has_and_belongs_to_many :keywords, 
:class_name=>"Keyword", 
:join_table=>"product_keywords", 
:foreign_key=>"product_id", 
:association_foreign_key=>"keyword_id", 
:select=>"keywords.*",
:insert_sql=>'INSERT INTO product_keywords (product_id, keyword_id) VALUES (#{id}, #{record.id})'

end

Friday, February 29 2008

Rails problems and errors : Expected ... to define ...


Expected /your/rails/dir/someclass.rb to define Someclass

It seems that this error appears when something is missing, to know what is missing exactly, try to run

 
$ script/console 

then


 >> Someclass

It will give you details about the problem.

Monday, January 28 2008

Un hosts.deny pour apache 2

Pour bloquer l'accès à un site web pour une adresse spécifique, j'ai d'abord tout simplement pensé à /etc/hosts.deny, mais pour aller plus loin j'avais besoins d'une solution plus fine. Après quelques recherches j'ai donc trouvé un moyen de le faire efficacement grâce au mod_rewrite d'apache.

On rajoute déjà les règles dans le fichier de son vhost :


RewriteEngine On

RewriteMap hosts-deny txt:/etc/apache2/hosts.deny
RewriteCond ${hosts-deny:%{REMOTE_HOST}|NOT-FOUND} !=NOT-FOUND [OR]
RewriteCond ${hosts-deny:%{REMOTE_ADDR}|NOT-FOUND} !=NOT-FOUND
RewriteRule ^/.* - [F]

Cette règle lit dans le fichier /etc/apache2/hosts.deny afin de lister les adresses ip ou domaines à bloquer :


uneip -
unnomdedomaine -
 

Il ne manque plus qu'a redémarrer apache :


/etc/init.d/apache2 restart

Les visiteurs étant listés dans /etc/apache2/hosts.deny se verront ainsi renvoyer vers une page "Forbiden". On peut bien sur imaginer une redirection vers une page personnalisée genre (banni.html) :


<html>
<head>
<title>Vous avez été banni</title>
</head>
<h1>Vous n'êtes plus le bienvenu ici</h1>
</html>

On change simplement la ligne :


RewriteRule ^/.* - [F]

Par :


RewriteRule ^/.* /banni.html [L]

Source: http://httpd.apache.org/docs/2.0/mi...

Service web rails pour Second Life (2/2)

Pour continuer sur les traces du précédent article, intéressons nous à nouveau à Second Life et Ruby on Rails. Dans cet article, nous allons voir comment communiquer depuis un objet dans le jeu vers un site Rails. Plus simplement, nous n'allons cette fois pas utiliser le système de webservice de rails, mais uniquement les arguments d'url. Ce service minuscule aura juste pour but de convertir tous les caractères en majuscule.

Cette fois nous commençons donc par la partie LSL :


string texte="Hello World";
string texte_majuscule;

default
{
  state_entry()
    {
    
     string resultat;
     llHTTPRequest("http://monserveur.fr/second_life/majuscule", [HTTP_METHOD, "PUT",HTTP_MIMETYPE,"application/x-www-form-urlencoded"], "texte="+(string)texte);

    }

  http_response(key request_id, integer status, list metadata, string body) {
    texte_majuscule=body;
  }

}

Le résultat est récupéré dans la fonction http_response.

La partie rails est bien plus simple encore :

class SecondLifeController < ApplicationController
 def majuscule
   render :text=>params[:texte].upcase
 end
end

Bien sur il ne s'agit que d'un exemple, je suis sur que vous aurez de bien meilleurs idées que moi!

Wednesday, July 11 2007

Second Life web service with rails (1/2)

In this first part, we will see how to communicate with an in-world object from a ruby on rails application thanks to XML-RPC. At the beginning, we define a second life structure (app/models/second_life_struct.rb) which represent the API format of second life :


class SecondLifeStruct < ActionWebService::Struct
  member "Channel", :string
  member "StringValue", :string
  member "IntValue", :int
end

Next, we create a new rails API (app/apis/second_life_api.rb) which use the structure :

class SecondLifeApi < ActionWebService::API::Base
  inflect_names false
  api_method "llRemoteData", :expects=>[SecondLifeStruct], :returns=>[SecondLifeStruct]
end

Before continuing, let's see the object LSL script which receive communication :


default
{
  state_entry()
    {
     llOpenRemoteDataChannel(); 
    }

    remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) 
    {
      if(type==REMOTE_DATA_CHANNEL) {
	llOwnerSay("Communication channel: "+(string)channel);
      } 

      if(type==REMOTE_DATA_REQUEST) {
        llOwnerSay(sval);
        llRemoteDataReply(channel,message_id,"Everything is alright",0);
      }
    }
}

Communication channel is an unique key which permit to communicate back with this object from the Linden's webservice. We must copy this key for loading our rails web page (/second_life/test_object?channel=key)

We just have to define the controller now:

class SecondLifeController < ApplicationController
  web_client_api :second_life, :xmlrpc, "http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi", :timeout=>90

  def test_object
    canal=params[:channel]
    begin
      resultat=second_life.llRemoteData(SecondLifeStruct.new("Channel"=>canal,"StringValue"=>"Hello World!","IntValue"=>"0"))
      render :text=>resultat["StringValue"]
    rescue
     render :text=>"Object doesn't respond"
    end
  end
end

That's all! When we load the page, second life object says "Hello World!" In the next post, we will see how to communicate in the other way : from in-world object to rails application.

More informations about second life XML-RPC format here.

Wednesday, June 13 2007

Service web rails pour Second Life (1/2)

Dans cette première partie, nous verrons comment communiquer avec un objet in-world depuis une application ruby on rails grâce à XML-RPC. Pour commencer on définit une structure second life (app/models/second_life_struct.rb) qui représentera le format de l'api second life :

class SecondLifeStruct < ActionWebService::Struct
  member "Channel", :string
  member "StringValue", :string
  member "IntValue", :int
end

Ensuite, on créé une nouvelle api (app/apis/second_life_api.rb) qui utilise cette structure:

class SecondLifeApi < ActionWebService::API::Base
  inflect_names false
  api_method "llRemoteData", :expects=>[SecondLifeStruct], :returns=>[SecondLifeStruct]
end

Avant de continuer, intéressons nous rapidement au script LSL de l'objet qui permettra de recevoir la communication :

default
{
  state_entry()
    {
     llOpenRemoteDataChannel(); 
    }

    remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) 
    {
      if(type==REMOTE_DATA_CHANNEL) {
	llOwnerSay("Canal de communication : "+(string)channel);
      } 

      if(type==REMOTE_DATA_REQUEST) {
        llOwnerSay(sval);
        llRemoteDataReply(channel,message_id,"Tout va bien",0);
      }
    }
}

Le canal de communication correspond à un identifiant unique permettant de communiquer par la suite avec cet objet depuis le service web de Linden. Il faudra copier cet identifiant pour pouvoir appeler notre page dans rails (/second_life/tester_objet?canal=identifiant).

Il ne reste plus qu'à définir le controlleur :

class SecondLifeController < ApplicationController
  web_client_api :second_life, :xmlrpc, "http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi", :timeout=>90

  def tester_objet
    canal=params[:canal]
    begin
      resultat=second_life.llRemoteData(SecondLifeStruct.new("Channel"=>canal,"StringValue"=>"Bonjour tout le monde !","IntValue"=>"0"))
      render :text=>resultat["StringValue"]
    rescue
     render :text=>"L'objet ne répond pas"
    end
  end
end

Et voila, lorsque l'on se connecte à la page, l'objet dans second life nous dira "Bonjour tout le monde !".

Comme on vient de le voir, le système de service web de ruby on rails permet d'interfacer facilement votre application à second life. Le prochain article montrera comment faire l'opération inverse : communiquer depuis un objet second life vers une application rails.

Pour plus d'information sur le format XML-RPC de second life, voir ici.

Friday, April 6 2007

Rails vite fait : empêcher les bots de créer des sessions

Quand le traffic d'un site augmente; il y a fort à parier que googlebot, msnbot et d'autres viennent y jeter un coup d'oeil. Le problème est que ces bots ne connaissent pas les cookies, et font générer une nouvelle session à rails à chacune de leurs visites. Ce qui peut vite devenir un cauchemar pour le répertoire tmp/sessions.

Pour éviter cela, on peut empêcher les bots de créer une session, il suffit de déterminer si le visiteur est un bot et de désactiver les sessions. Dans le contrôleur :


 session :off, 
  :if => Proc.new { |request| request.user_agent =~ /(Baidu|Gigabot|Googlebot|Exabot|QihooBot|crawler|Crawler|Spider|Nutch|psbot|Seekbot|MJ12bot|IRLbot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)/i }

Il s'agit bien sur d'une liste non exhaustive des bots existants. Vous pouvez en trouver d'autres en étudiant un peu les logs de votre site.

Source (en) : http://gurge.com/blog/2007/01/08/turn-off-rails-sessions-for-robots/

Sunday, February 11 2007

Rails vite fait : détecter si l'utilisateur a activé les cookies

Rails utilise activement les cookies, notamment pour gérer les sessions (variable @session). Si l'utilisateur a désactivé les cookies dans son navigateur, on se retrouve alors face à de nombreux problèmes. Pour y pallier, on se propose de forcer l'utilisateur à activer ses cookies.

On définit une fonction de vérification dans son contrôleur :


 def cookies_required
    if @request.cookies["_session_id"].to_s == ''
      if @params[:cookies_enabled].nil?
        redirect_to :controller=>@params[:controller],
        :action=>@params[:action],
        :cookies_enabled=>"testing"
      else
        render :text=>"Vous devez activer les cookies pour utiliser ce site!"
      end
    end
  end

Puis on l'applique avant chaque action grâce à before_filter :


  before_filter :cookies_required

Rails vite fait : forcer https pour certaines pages

On peut laisser à rails le soin de gérer le protocole de certaines pages. Par exemple une page d'identification. Pour se simplifier la tâche, on utilise le before_filter du contrôleur. On commence par écrire notre fonction de redirection dans le contrôleur :


 def redirect_to_ssl
    redirect_to :protocol => "https://" unless (@request.ssl?)
  end

Ensuite, on applique notre filtre aux pages que l'on veut :


 before_filter :redirect_to_ssl, :only=>[:login]

Et voilà : http://monsite.fr/controller/login se transformera en https://monsite.fr/controller/login.

Thursday, January 18 2007

Rails vite fait : gestion de thèmes

On imagine un système simple de gestion de thèmes dans lequel on passe le nom du thème dans une variable de session.

Tout d'abord on créé un répertoire de thèmes à la racine de son application :

mkdir /repertoire/du/site/rails/themes

Pour chaque thème, on a un sous-répertoire dans themes. Par exemple un thème standard :

mkdir /repertoire/du/site/rails/themes/standard

Enfin pour notre thème on a un layout : standard/layout.rhtml Dans notre contrôleur, on définit une méthode de thème :

def theme_layout
"../../themes/"+@session[:theme]+"/layout"
end

A présent on peut utiliser le thème par render :

render :use_full_path=>true, :layout=>:theme_layout

ou alors dans tout le contrôleur :

class MonController < ApplicationController
layout :theme_layout
end

On peut définir quel thème utiliser n'importe où avec la variable de session :

@session[:theme]="standard"

Rails vite fait : cache et nom de page contenant un point

Les noms de page contenant un point (par exemple dans une action tag/:name pour un tag "Web-2.0") ne sont pas correctement mises en cache avec cache_pages. Voici un petit hack vite fait pour régler le problème :


caches_page :tag

after_filter {|c| 
      path="public"+c.request.env["PATH_INFO"]
      if File.exists?(path) and !File.directory?(path)        
        File.rename(path,path+".html")
      end
  }

Rails vite fait : liste des pays en français

Il peut être pratique de pouvoir utiliser la fonction de helper country_select avec les noms de pays en français. Impossible de trouver une solution toute faite et rapidement applicable sur internet.

La voici donc, en utilisant la liste du site http://www.iso.org. Je vous passe les détails du script de conversion, voici directement le résultat, à mettre dans votre config/environment.rb :



ActionView::Helpers::FormOptionsHelper::COUNTRIES.replace [
"Afghanistan", "Afrique du sud", "Åland, îles", "Albanie", "Algérie", "Allemagne", "Andorre", "Angola", "Anguilla", "Antarctique", 
"Antigua-et-barbuda", "Antilles néerlandaises", "Arabie saoudite", "Argentine", "Arménie", "Aruba", "Australie", "Autriche", 
"Azerbaïdjan", "Bahamas", "Bahreïn", "Bangladesh", "Barbade", "Bélarus", "Belgique", "Belize", "Bénin", "Bermudes", "Bhoutan", "Bolivie", 
"Bosnie-herzégovine", "Botswana", "Bouvet, île", "Brésil", "Brunéi darussalam", "Bulgarie", "Burkina faso", "Burundi", "Caïmanes, îles", 
"Cambodge", "Cameroun", "Canada", "Cap-vert", "Centrafricaine, république", "Chili", "Chine", "Christmas, île", "Chypre", "Cocos 
(keeling), îles", "Colombie", "Comores", "Congo", "Congo, la république démocratique du", "Cook, îles", "Corée, république de", "Corée, 
république populaire démocratique de", "Costa rica", "Côte d'ivoire", "Croatie", "Cuba", "Danemark", "Djibouti", "Dominicaine, 
république", "Dominique", "Égypte", "El salvador", "Émirats arabes unis", "Équateur", "Érythrée", "Espagne", "Estonie", "États-unis", 
"Éthiopie", "Falkland, îles (malvinas)", "Féroé, îles", "Fidji", "Finlande", "France", "Gabon", "Gambie", "Géorgie", "Géorgie du sud et les îles 
sandwich du sud", "Ghana", "Gibraltar", "Grèce", "Grenade", "Groenland", "Guadeloupe", "Guam", "Guatemala", "Guernesey", "Guinée", 
"Guinée-bissau", "Guinée équatoriale", "Guyana", "Guyane française", "Haïti", "Heard, île et mcdonald, îles", "Honduras", "Hong-kong", 
"Hongrie", "Île de man", "Îles mineures éloignées des états-unis", "Îles vierges britanniques", "Îles vierges des états-unis", "Inde", 
"Indonésie", "Iran, république islamique d'", "Iraq", "Irlande", "Islande", "Israël", "Italie", "Jamaïque", "Japon", "Jersey", "Jordanie", 
"Kazakhstan", "Kenya", "Kirghizistan", "Kiribati", "Koweït", "Lao, république démocratique populaire", "Lesotho", "Lettonie", "Liban", 
"Libéria", "Libyenne, jamahiriya arabe", "Liechtenstein", "Lituanie", "Luxembourg", "Macao", "Macédoine, l'ex-république yougoslave de", 
"Madagascar", "Malaisie", "Malawi", "Maldives", "Mali", "Malte", "Mariannes du nord, îles", "Maroc", "Marshall, îles", "Martinique", "Maurice", 
"Mauritanie", "Mayotte", "Mexique", "Micronésie, états fédérés de", "Moldova, république de", "Monaco", "Mongolie", "Monténégro", 
"Montserrat", "Mozambique", "Myanmar", "Namibie", "Nauru", "Népal", "Nicaragua", "Niger", "Nigéria", "Niué", "Norfolk, île", "Norvège", 
"Nouvelle-calédonie", "Nouvelle-zélande", "Océan indien, territoire britannique de l'", "Oman", "Ouganda", "Ouzbékistan", "Pakistan", 
"Palaos", "Palestinien occupé, territoire", "Panama", "Papouasie-nouvelle-guinée", "Paraguay", "Pays-bas", "Pérou", "Philippines", 
"Pitcairn", "Pologne", "Polynésie française", "Porto rico", "Portugal", "Qatar", "Réunion", "Roumanie", "Royaume-uni", "Russie, fédération 
de", "Rwanda", "Sahara occidental", "Sainte-hélène", "Sainte-lucie", "Saint-kitts-et-nevis", "Saint-marin", "Saint-pierre-et-miquelon", 
"Saint-siège (état de la cité du vatican)", "Saint-vincent-et-les grenadines", "Salomon, îles", "Samoa", "Samoa américaines", "Sao 
tomé-et-principe", "Sénégal", "Serbie", "Seychelles", "Sierra leone", "Singapour", "Slovaquie", "Slovénie", "Somalie", "Soudan", "Sri lanka", 
"Suède", "Suisse", "Suriname", "Svalbard et île jan mayen", "Swaziland", "Syrienne, république arabe", "Tadjikistan", "Taïwan, province de 
chine", "Tanzanie, république-unie de", "Tchad", "Tchèque, république", "Terres australes françaises", "Thaïlande", "Timor-leste", 
"Togo", "Tokelau", "Tonga", "Trinité-et-tobago", "Tunisie", "Turkménistan", "Turks et caïques, îles", "Turquie", "Tuvalu", "Ukraine", 
"Uruguay", "Vanuatu", "Venezuela", "Viet nam", "Wallis et futuna", "Yémen", "Zambie", "Zimbabwe"
]

Monday, November 13 2006

Transformer un Iomega high-speed ethernet en véritable NAS

J'ai acheté, il y a quelques temps déjà, un disque réseau Iomega bas de gamme. Plutôt pratique à première vue, il permet de charger son disque à travers samba ainsi que de modifier quelques options avec une interface de gestion html.

Vu le prix de celui-ci, rien de bien extraordinaire. Pourtant un certain nombre de limitations m'empêchaient de profiter pleinement de l'engin. Tout d'abord, pas de partage nfs, le partage samba c'est bien quand on a windows, mais sinon, pas de permissions. En plus aucun droit d'accès au disque: on y accède directement avec tous les droits de lecture/écriture sans mot de passe. Enfin Iomega ne supporte déjà plus ce disque (il n'est d'ailleurs même plus sur leur site) donc pas de nouveau firmware à esperer.

En cherchant un peu sur internet, j'ai découvert qu'en fait, la machine tournerait sous linux. En poursuivant mes recherches (démontage de l'appareil à l'appui), je découvre qu'en fait le système embarqué est bien un linux et qu'il s'agit d'une carte Axis Etrax 100LX. Avec un CPU RISC 32bits à 100Mhz, 32 Mo de RAM et surtout un environnement de cross-compilation et un tas d'outils linux disponibles en téléchargement sur le site d'Axis!

Problème: le truc de Iomega est totalement fermé. Pas d'accès autre que samba et l'interface de gestion, et aucun autre signe de configuration possible du linux à l'interieur. Un serveur ftp semble tourner dessus mais  impossible de s'y connecter car je ne dispose pas du mot de passe vu que Iomega semblerait garder ce ftp uniquement au cas ou ils voudraient permettre une mise à jour du firmware. En conclusion, j'ai acheté un système sur lequel je n'ai pas les droits d'accès !l

Après avoir tourné le problème dans tous les sens, j'ai finalement trouvé une solution d'une effrayante simplicité.
Sachant qu'il s'agit d'un linux qui fait tourner samba, je sais que samba suit les liens symboliques unix pour les présenter en dur une fois le disque monté : il me suffit donc de créer un lien symbolique vers / depuis le répertoire que je peux voir dans samba!
Pour cela il faut démonter le disque et le remonter dans un PC (PATA), monter la bonne partition, ajouter le lien puis tout remettre comme avant (j'imagine que le fait de démonter le disque enlève la garantie, mais bon osef).

Une fois le disque enlevé de sa boite, on le met dans un PC. Imaginons qu'on le monte en esclave, alors on le verra apparaître en hdb dans linux. On découvre qu'il y a deux partition. A prioris /dev/hdb1 est une petite partition de 6Mo ou le firmware se colle au démarrage. /dev/hdb2 contient nos données: c'est là où l'on mettra notre lien.

# mount /dev/hdb2 /tmp
# cd /tmp
# ln -s / Root
# umount /tmp

Voila, ça c'est fait. On remonte le tout comme avant: on a maintenant accès à l'arborescence du système dans Root lorsque l'on monte le disque réseau avec samba.
Tout cela est bien beau, mais on ne peut pas faire grand chose de plus pour l'instant. Le top serait d'avoir un ssh ou un telnet pour pouvoir éxécuter à souhait des trucs dessus (je vous laisse imaginer).

Il faut savoir que le système en lui même est en lecture seulement. Seul le répertoire /etc est en lecture/écriture. C'est déjà pas mal! On va pouvoir installer un ssh et un nfs qui se lancent au démarrage. N'oublions pas que nous avons tout l'environement de cross-compilation à disposition !

La première étape consisterait en un remplacement du /etc/passwd, avec un mot de passe root que l'on connait. Ainsi on pourait déjà accéder au ftp.

Pour le reste, bon courage. :)


Saturday, October 21 2006

Mise à jour dotclear2

J'ai fini la migration typo 2.6 à dotclear2. Plusieurs raisons à ça:
  • notre machine a du mal à supporter ruby avec ses 256Mo de ram, du coup, ralentissement pour tous mes colloc's
  • dotclear2 est vraiment génial.
J'attends avec impatience de pouvoir me prendre une dedibox. Bien meilleurs rapport qualité/prix que notre solution actuelle.
Enfin, j'en ai profité pour faire un nouveau thème à partir de celui par défaut de dotclear.
Et c'est tout!

Wednesday, October 4 2006

Déploiement de ruby on rails avec apache 2.0 et mongrel

Le principal problème de Ruby on Rails, actuellement, est son déploiement dans un environnement de production. Il existe pourtant un certain nombre de possibilités plus ou moins simples.

La solution que j’ai retenu est d’utiliser la capacité de clustering de mongrel avec la version 2.0 de apache . Les développeurs de mongrel conseillent l’utilisation d'apache 2.2 et de son mod_load_balancer. Néammoins, apache 2.0 reste la version la plus simple à utiliser, car maintenue par les principales distributions (Debian en tête).

En imaginant que nous avons déjà un environnement rails en place, nous allons commencer par installer mongrel:


$ sudo gem install mongrel

Une fois mongrel installé, configurons le pour une application donnée :


$ sudo mongrel_rails cluster::configure -e production \
-p 8000 -N 3 -c /repertoire/de/notre/application \
-a 127.0.0.1 --user www-data --group www-data
Petite explication, nous configurons mongrel en cluster de trois processus (-N 3) commençant au port 8000 (-p 8000), ce qui nous lancera trois processus (8000,8001 et 8002). A noter également que notre répertoire devra posséder les permissions www-data. Nous pouvons à présent lancer le cluster:

$ sudo mongrel_rails cluster::start

Il faut maintenant configurer apache pour qu’il gère de manière transparente ce cluster. Pour cela, nous allons utiliser le module mod_proxy de la version 2.0. Voici le code pour un hôte virtuel (/etc/apache2/site-availables/monsite.fr dans Debian):


<VirtualHost *>
ServerName www.monsite.fr
ServerAdmin admin@monsite.fr
DocumentRoot /repertoire/de/notre/application/public

<Proxy *>
Order deny,allow
Allow from all
</Proxy>

ProxyRequests Off
ProxyPassReverse / http://localhost:8000/
ProxyPassReverse / http://localhost:8001/
ProxyPassReverse / http://localhost:8002/
ProxyPreserveHost On
RewriteEngine On
RewriteMap servers rnd:/etc/apache2/map.txt
RewriteRule ^/(images|stylesheets|javascripts)/?(.*) $0 [L]
RewriteRule ^/(.*)$ http://localhost:${servers:ports}/$1 [P,L]

<Directory /repertoire/de/notre/application/public/>
Options +FollowSymLinks
Order allow,deny
allow from all
</Directory>

ErrorLog /var/log/apache2/monsite_error.log
LogLevel warn
CustomLog /var/log/apache2/monsite_access.log combined
ServerSignature On
</VirtualHost>

N’oubliez pas d’activer le module apache et le nouveau site.


$ sudo a2enmod proxy
$ sudo a2ensite monsite.fr

Il ne reste plus qu’à relancer apache! Les connexions vers www.monsite.fr seront ainsi redirigées automatiquement vers le cluster le plus approprié.

Cette technique nous permet d’avoir une application rails supportant une charge plus importante en multipliant les clusters.

Voir également (en anglais): la doc de mongrel et ce blog.

Monday, July 24 2006

Passer une commande en tâche de fond

Quel désespoir de voir la commande qu’on a lancé à midi ne pas être terminée au moment de quitter le travail… Heureusement unix est la pour nous éviter de faire des heures sup’!

Disons que la commande est lancée depuis un terminal X par un shell ouvert en ssh :

julbouln@lesitedistant:~$ commande

On commence par susprendre la tâche avec un CTRL+Z :

[1]+  Stopped      commande
julbouln@lesitedistant:~$

On passe celui-ci en tâche de fond :

julbouln@lesitedistant:~$ bg

Le problème à présent est que la commande est toujours associée au terminal, donc si on quitte le shell, la commande se termine. Heureusement il existe une commande pour la détacher du terminal :

julbouln@lesitedistant:~$ disown

Et voila! On peut à présent quitter en laissant la commande se terminer tranquillement.

Monday, June 19 2006

Client Last.fm embarqué

Cela faisait longtemps que mon iPaq trainait sur mon bureau sans que je puisse y trouver une très grande utilité. L’espace étant très limité sur ce modèle (h3600), je ne pouvais pas y installer grand chose.

C’est en utilisant Last.fm que j’ai trouvé une application originale: le transformer en radio Last.fm portative. En effet, la petite carte WiFi permettant à l’engin de se connecter à internet depuis n’importe quel hotspot.

J’ai donc fouillé un peu sur internet pour voir s'il n’existait pas déjà un client Last.fm pour cette plateforme. En vain. Pensant simplement porter le client officiel, je me suis rendu compte que Opie utilisait encore Qt3, alors que le client, la version 4.

C’est alors que je suis tombé sur un projet intéressant, Shell.FM, celui ci propose un client Last.fm entièrement en ligne de commande. Pour le cas d’une application embarquée, c’est assez délicat de contrôler un programme en mode console avec un stylet et un clavier virtuel. J’ai donc décidé de reprendre les sources de Shell.FM pour en faire un client avec GPE .

Malgré plusieurs petits problèmes, je suis tout de même parvenu à un resultat plutôt satisfaisant !

Wednesday, June 14 2006

Ajouter un script au démarrage de la Debian

Il peut être pratique de rajouter un script personnalisé au démarrage. Pour ce faire, la Debian propose un outil de haut niveau pour faciliter la tâche : update-rc.d.

Tout d’abord, il vous faut créer un script que vous placerez dans /etc/init.d.
MAJ: Il existe un fichier /etc/init.d/skeleton qui peut servir de modèle (merci tito).
Appelons le /etc/init.d/monscript par exemple :

#!/bin/sh
case "$1" in
start|"")
# au démarrage
;;
stop)
# à l'arret
;;
*)
echo "Usage: monscript [start|stop]" >&2
exit 3
;;
esac

Notez le case ”$1” qui recupère le premier argument de la commande. Si l’argument est start ou qu’il n’y en a pas, alors on exécute ce qu’il y a au niveau de # au démarrage. Si l’argument est stop, ce qu’il y a au niveau de # à l’arret

Rajoutez lui le mode exécutable :

# chmod +x /etc/init.d/monscript
Vous pouvez à présent tester votre script directement depuis la console :

# /etc/init.d/monscript stop
# /etc/init.d/monscript start
Ensuite, afin qu’il se lance automatiquement au démarrage, il faut le rajouter aux runlevels. Pour cela, utilisez simplement la commande update-rc.d :

# update-rc.d monscript defaults 99

update-rc.d possède un certain nombre d’options, nous utilisons les plus simples dans cet exemple :

  • monscript correspond au fichier bash /etc/init.d/monscript que nous venons de créer
  • defaults permet de couvrir tous les runlevels
  • 99 est le niveau de priorité. 99 étant le plus faible et 1 le plus élevé. Le niveau de priorité correspondant à l’ordre lors du démarrage, les plus faibles étant exécutés en dernier.

Vous n’avez plus qu’à redémarrer pour essayer le tout!

Thursday, May 18 2006

Application de Google Maps

Google propose aux développeurs une API très pratique pour son Google Maps . J’en ai fait une application pratique et professionnelle pour les éditions O’Reilly .

L’idée était de permettre aux visiteurs et éventuels lecteurs de trouver rapidement et facilement les libraires ayant un livre donné autour de chez eux.

Par exemple, les Nancéens pourront facilement trouver VoIP à 200% chez leurs libraires locaux!

Ce système, utilisant notre base interne, propose une probabilité de la présence d’un livre chez un libraire.

Le principal problème de l’API Google Maps et qu’il ne permet pas de placer une position sur une carte directement à partir d’une adresse . Il faut donc déterminer par nos propres moyens les latitudes/longitudes des endroits que l’ont veut placer. Bien qu’il existe un outil pour faire cela simplement quand il s’agit d’adresses américaines, c’est une autre paire de manches pour nous européens.

Dans un premier temps, il nous a fallu déterminer les positions de chacun des libraires avec qui nous travaillons, puis j’ai du convertir des positions des communes de la Poste qui étaient dans le système de projection Lambert II vers le système de Google (gws84).

J’ai fait cette conversion relativement facilement grâce au logiciel open source Proj4 . Vous comprendrez le “relativement” en voyant la commande qui permet cela:

cs2cs -f '%.3f' +proj=lcc +lat_0=46.8 +lat_1=45.898918 +lat_2=47.696014 +lon_0=2.3372291 \ 
+k_0=0.99987742 +x_0=600000 +y_0=2200000 +a=6378249.2 +b=6356515 +ellps=clrk80 \
+towgs84=-168,-60,320,0,0,0,0 +to +proj=latlong +datum=WGS84 +ellps=WGS84 +no_defs -s

Cette commande prennant les x,y des coordonnées Lambert II dans le pipe unix et retournant les coordonnées pour google.

Un second problème et la compatibilité des navigateurs. Avec firefox, aucun problème, on peut coder comme on le sent. Pour ceux voulant permettre une compatibilité avec IE, il faudra y penser dès le départ et adapter le code en conséquence.

Pour le reste, l’API Google Maps reste simplissime d’utilisation et configurable au plus haut point: icones des points et des ombres personnalisées, choix des contrôleurs dans l’interface ou encore récupération des événements de l’utilisateur.

Sunday, April 30 2006

Installer Windows après une Debian

Il arrive parfois que l’on veuille installer un Windows sur une machine où Linux est déjà installé. Le problème est que Windows efface le chargeur multi-os pour se mettre à la place.

Heureusement il existe une technique rapide et simple pour réinstaller le chargeur.

Prenons un exemple, vous avez un disque partionné comme suit:

/dev/hda1 : ext3 monté en / contenant Linux
/dev/hda2 : swap
/dev/hda3 : ext3 monté en /home
/dev/hda4 : ntfs ou vfat contenant Windows

Après avoir installé Windows, le système sur /dev/hda4 est chargé automatiquement au démarrage. Pour y remédier, il suffit d’utiliser le cd d’installation de Debian (un cd netinstall suffit).

Mettez le dans le lecteur et faites comme si vous alliez installer la Debian. Vous pouvez sélectionner les options de configuration pour votre clavier. Ensuite, basculez dans la deuxième console (ALT-F2) et tappez entrée pour avoir un prompt.

A présent, il vous faut charger votre partition Linux:

mount -t ext3 /dev/hda1 /tmp

Dans l’installeur Sarge, il semble que les fichiers de disque n’ont pas le même shéma, si vous ne trouvez pas de /dev/hda1, utilisez /dev/ide/host0/bus0/target0/lun0/part1 à la place.

Pour utiliser les commandes de votre propre système :

chroot /tmp

Réinstallez le chargeur multi-os :

grub-install /dev/hda

Et voila, vous devriez avoir un message indiquant que tout c’est bien passé ! Quittez votre système :

exit

Démontez le disque :

umount /tmp

Et redémarrez :

reboot

Au redémarrage, vous allez avoir le même écran de démarrage qu’avant avoir installé Windows. Démarrez votre Linux, vous pouvez rajouter l’entrée Windows à la main dans /boot/grub/menu.lst :

title       Windows NT/2000/XP
root (hd0,3)
makeactive
chainloader +1

ou lancer un dpkg-reconfigure grub, ce qui devrait détecter et intégrer directement le nouveau système.

Thursday, April 6 2006

Mise à jour

Bon voilà c’est fait. J’ai craqué pour un nouveau style. Codé par mes petites mains musclées et malgré les nombreuses critiques de tous ceux à qui j’ai demandé l’avis… J’avais envie de fun, de couleurs, de vie ! C’est le printemps ! Les oiseaux chantent, les pigeons roucoulent… Tout ça.

A noter également la traduction “vite fait” de Typo. Allez les gars, pensez au reste du monde et faites nous un joli truc localisé!

Et c’est tout.

Saturday, April 1 2006

Last.fm GPE Player

About

GPE.FM is a mini Last.fm player designed for embedded device. It is a quick made small hack using Shell.FM sourcecode and GTK/GPE toolkit.

Since everyone loves screenshots :


Dependencies

To use it, you need:

Download

You can either download the source code or the pre-compiled binary for ARM familiar distribution .

À propos


    Julien Boulnois, 26 ans, actuellement administrateur système et réseau chez les Editions O'Reilly. Je suis spécialisé dans le domaine de l'open source. Plus qu'un metier, l'informatique est pour moi une passion depuis plus de 10 ans. Je m'intéresse à beaucoup de domaines qui touchent de près ou de loin aux nouvelles technologies. Systèmes et réseaux, programmation ou multimédia, je suis un technophile averti et un geek. Mon domaine de prédilection reste néammoins l'open source que je pratique aussi bien pour mon utilisation personnelle que professionnelle depuis de nombreuses années. Vous pouvez récupérer mon CV et me contacter.