terça-feira, 6 de março de 2012

Apache – tutorial mod_rewrite


Introdução

Conforme descrito nessa referência:
“a rule-based rewriting engine to rewrite requested URLs on the fly.”
O mod_rewrite é complexo, faz um uso sem fronteiras de regex e complica a vida de muito administrador; a minha, por exemplo.
Não sou nem especialista nem usuário intermediário. Na verdade estou coletando as informações para esse post conforme estou estudando esse módulo do Apache. Como mais uma vez o que preciso está distribuido em muitos lugares diferentes, resolvi fazer uma coletânea e descrever ao meu ponto de vista (com algumas cópias de texto, com as referências devidamente citadas) diversas regras.
No http.conf deve ter uma linha referenciando o módulo. Se não tiver ou se estiver comentada (com uma cerquilha na frente) então faça com que fique assim:

LoadModule rewrite_module modules/mod_rewrite.so

Reinicie o apache (em Debian nesse exemplo):

/etc/init.d/apache2 restart
Existem duas maneiras possíveis para utilização do módulo em questão. Uma bastante utilizada é escrevendo no .htaccess. Na referência que peguei (no primeiro link desse post) as primeiras linhas a escrever no arquivo são (assim o fiz no virtualhost default):

<IfModule mod_rewrite.c>
Options +FollowSymLinks
Options +Indexes
RewriteEngine On
</IfModule>
A primeira, por questão de segurança é obrigatória; mas foge ao meu entendimento do porque ter que escrevê-la uma vez que é obrigatória.
A segunda linha é para o caso de utilização de indexes.
A terceira fala por sí só.
Para configurar a URL base do seu server, utiliza-se algo assim:

RewriteBase /
A aplicação se tornará mais clara, então não se preocupe com a introdução até esse momento.
A utilização mais básica do rewrite tem o formato:

RewriteRule PATTERN DESTINATION
Por exemplo, http://algumacoisa.com/ contendo a seguinte regra:

RewriteRule /alvoSolicitado.html /arquivoDeDestino.html

Isso redirecionará a requisição do arquivo alvoSolicitado.html para arquivoDeDestino.html. Básico e pouco útil por enquanto, não? Mas vai complicar, não se preocupe.
Expressões regulares
Redirecionar uma página x para uma página y não é tão útil ou aplicável no dia a dia. Na verdade as configurações tendem a um conjunto de regras interpretadas pelo módulo. Essas regras são compostas por passos que em diversas de suas linhas se aplicam expressões regulares.
Se você já está familiarizado com expressões regulares, siga a leitura daqui, senão, nos vemos outro dia.
Supondo um arquivo com php que receba um GET na variável dia de uma agenda:

http://algumacoisa.com/agenda.php?dia=9

Obviamente para o usuário não é simples lembrar uma URL assim. Mas também por questões de estética e até por questões de segurança o formato ideal seria:

http://algumacoisa.com/agenda/9

Agora aquela regra básica fará mais sentido:

RewriteRule ^/agenda/9$ /agenda.php?dia=9
A expressão regular aplicada foi a mais simples possível. O circunflexo indica “que começa” e o cifrão indica “que termina”, ou seja, quando for exatamente http://algumacoisa.com/agenda/9 ele deve ser redirecionado para http://algumacoisa.com/agenda.php?dia=9.
Então, para fazer a agenda do mês, basta fazer 31 linhas como esta. Certo? – claro que não.
As expressões regulares são assustadoras mas deliciosamente maleáveis. Para fazer a agenda do mês (sem muito requinte, pois isso aqui é um exemplo de uso, nada mais) a regra ficaria assim:

RewriteRule ^/agenda/([0-3]+[0-9])$ /agenda.php?dia=$1
Com essa regra estamos preparados para um mês com até 39 dias! Ok, não ficou perfeito, mas serve para o exemplo de uso.
A regra diz que tudo que começar com agenda/1,2,3 e seguir de 0 até 9, deve-se redirecionar para agenda.php para o dia $1. O $1 quer dizer a primeira referência da expressão, que seria justamente o dia enviado na requisição. Se houvessem outros parãmetros, então a expressão se extenderia e sua referência também seria posicional no redirecionamento, aplicando $2, $3, etc.
Uma outra regra muito interessante é a expansão de palavras na requisição. Como pode-se notar, passamos o dia mas não temos referência de mês. Para tal, exemplico com a seguinte regra:

RewriteRule ^/agenda/(([0-3]+[0-9])(.*))\.html$ /agenda.php?data=$1

Repare que agora a expressão para um mês de 39 dias segue com “.*”. Isso significa que qualquer coisa a partir daquele ponto é permitida, desde que seguida pela extensão html. A barra invertida foi utilizada para quebrar o significado da expressão de “.”, que indica a posição atual. Com a barra invertida, ponto significa ponto mesmo. E ponto.
Para finalizar, o redirecionamento aponta para a variável data, que recebe o que casar com a expressão como parâmetro.
Também deve-se notar que toda a expressão foi fechada em parênteses, senão teriamos 2 parâmetros em data; $1 com o dia e $2 com o restante da string; agora o parâmetro $2 seria apenas a extensão (‘.html’).
Uma url assim seria válida para esse caso:

http://algumacoisa.com/agenda/09-02-12.html
#resultaria em:
http://algumacoisa.com/agenda.php?data=09-02-12

Obviamente estes exemplos também casariam:

http://algumacoisa.com/agenda/09-Fevereiro-2012.html
http://algumacoisa.com/agenda/09-Banana-2012.html

Ainda está simples, mas as vezes as expressões regulares podem nos pregar peças e alguns resultados podem ser imprevisiveis. Então, a regra anterior necessita de mais requinte para tratar somente dados numéricos representando data, com até 2 bytes de comprimento e utilizando o separador “-”, porque no formato que está é uma regra falha.
Flags
Estou seguindo os mesmos passos do primeiro tutorial, inserindo mais ou menos informações conforme a ocasião.
Flags são utilizadas para alterar o comportamento da reescrita. As mais comuns são:
[L] – Last. Quando contida ao final de uma linha, o rewrite para o processamento nessa regra.
[R] – Redirect. Utilizada para enviar um código de redirect para o usuário.
[QSA] – Query String Append. Adiciona sua query string ao dado que já veio no get.
[NC] – No Case. Torna a expressão insensitiva.
Existem muitas outras flags para coisas mais específicas, mas vou seguir a âncora da referência; não quero lhe aborrecer com detalhes!
Enfim, aplicando algumas flags como exemplo (ainda está falha a regra):

RewriteRule ^/agenda/(([0-3]+[0-9])(.*))\.html$ /agenda.php?data=$1 [NC,L]
Nesse caso, o que casar com a expressão recebe o redirecionamento e o trabalho do rewrite para essa requisição termina aqui. Alguns exemplos que casariam:

http://algumacoisa.com/agenda/09-FeveReiRo-12.html
http://algumacoisa.com/agenda/09-fevereiro-12.html
http://algumacoisa.com/agenda/09-02-12.html

Reforço que no exemplo estou citando apenas os resultados esperados; diversas coisas inválidas casariam com essa regra.
RewriteCond
RewriteCond permite escrever condições que devem casar para que uma decisão seja tomada. Seu formato é:

RewriteCond STRING CONDITION

A STRING pode ser uma variável do servidor (vide lista de variáveis) ou uma backreference de outra RewriteCond.
A CONDITION pode ser outra expressão regular similar ao RewriteRule.
Então, uma RewriteRule só será executada se uma RewriteCond for atendida, se composta a regra dessa maneira:

RewriteCond %{REMOTE_ADDR} 123.45.67.89
RewriteRule .* limbo.html [R]

Nesse exemplo, a condição é que o host remoto tenha o IP 123.45.67.89. A regra que segue é “qualquer coisa” (.*), não importa o que seja, envie-o para o limbo(.html). Essa página pode conter qualquer mensagem como “você não pode ver nada aqui, mané”.
Algumas variáveis do servidor

HTTP_USER_AGENT
HTTP_REFERER
HTTP_HOST
SERVER_PORT
REMOTE_ADDR
SCRIPT_FILENAME
QUERY_STRING
REQUEST_URI
Esse material estava em meu rascunho já há algum tempo e não tive tempo ainda de elaborá-lo melhor, mas prometo que em algum tempo eu escrevo a parte 2 do mod_rewrite. Com certeza essa introdução lhe dará estrutura para avançar em diferentes exemplos. Bons testes!

sábado, 25 de fevereiro de 2012

Atualizando o Motorola Defy para Android Gingerbread (2.3)




Meu cunhado e minha cunhada tem um Motorola Defy, da operadora Claro. Estão bastante satisfeitos com a operadora e com o plano 3G. Mas como nada é perfeito, a ROM desse celular é horrível; travamentos, atraso na resposta da tela (que chega a impossibilitar o uso de teclado Swype), problemas com o GPS (de forma a travar o Maps). Para resolver esse problema, sugeri o uso de uma ROM da CyanoGenMod, que é a ROM que utilizo em meu idoso Milestone.
Certa vez sugeri escrever o procedimento, mas existem diversos tutoriais na internet, inclusive na própria CyanoGen. Então vou recomendar o procedimento de atualização do Defy, porque esse foge aos padrões. A ROM ainda é beta, mas com certeza centenas de vezes melhor que a ROM da operadora. O site do processo é o SuperDicas, com alguns “SuperErros” que impossibilitariam o uso da ROM. Então, antes de ler o tutorial deles, atenha-se aos procedimentos que deverão ser tomados.
Passo 3 – O botão de volume deverá ser apertado para CIMA e não para baixo, como descreve esse passo especificamente. Para os demais, o botão é para baixo mesmo.
Passo 4 (segunda rodada) – Sim, o tutorial foi escrito em etapas e essas etapas usam os mesmos números para passos diferentes. Nesse passo 4 da segunda rodada está detalhado o procedimento para fazer rooting no celular. O programa se torna irresponsível em diversos momentos, mas quando aparecer o box para encerrar o programa, mande aguardar. Insista nos cliques do botão de rooting, mesmo que pareça não surtir efeito. Se após muitas tentativas continuar sem efeito, feche o programa, abra-o novamente e repita o procedimento. Se achar necessário, reinicie o aparelho, mas não desista do rooting ou não será possível seguir adiante.
O pacote RSD está com o link apontando para os drivers USB. Para baixar o RSD, utilize esse link.
Achei estranho isso não estar descrito no tutorial e pensei que fosse um processo novo, mas há um erro em não descrevê-lo. Após instalar todos os pacotes conforme descreve o procedimento (lembrando que você não deve permitir a inicialização do sistema antes disso), faça mais um reboot no boot manager e vá até a opção wipe cache partition, confirme. Retornando ao menu, vá até a opção wipe data/factory reset e confirme. Com isso, todos os dados de usuário serão resetados e aparecerá um wizard muito legal para configurar seu aparelho como se fosse recém-tirado da caixa, além de evitar infinitos reboots (‘infinitos’ no sentido de que seu celular não tornará a carregar o sistema jamais, exceto se faça os procedimentos de wipe).
Após isso, esteja a vontade para configurar parâmetros antes indisponíveis, como configurar a cor do led para cada tipo de notificação do sistema, rotacionamento 360º, pré-visualização de multiplos windows (“beliscando” o centro da tela, como se quisesse pegar o papel de parede) e uma quantidade enorme de outros ítens no menu aplicativos, na categoria Cyanogen. Seu Defy será um novo smartphone, pode acreditar!

Replicação Master-to-Master com MySQL + HA com heartbeat


Essa solução é muito bem aplicada em ambientes que necessitem alta disponibilidade para serviço web e banco de dados.
Se sua necessidade é baseada em MySQL a partir da versão 5.5, leia esse outro post sobre a replicação com MySQL 5.5 e faça a parte do heartbeat a partir deste próprio.
O ambiente é montado por 2 servidores X (indiferente do hardware utilizado). O Software é basicamente um LAMP + heartbeat rodando em Debian e o tempo de configuração deve ser de uns 10 minutos.
Existem diversos artigos na internet de ambas soluções aplicadas aqui, porém decidi escrever um artigo sobre o assunto para que eu mesmo possa utilizado na posteridade, quando não me lembrar mais do procedimento.
Primeiros passos
Além da instalação do sistema LAMP (Linux, Apache, PHP, MySQL) e heartbeat será necessário separar 3 IPs fixos, sendo 1 para cada servidor e 1 para utilização de IP virtual. O IP virtual é o IP que será divulgado; as solicitações vindas dos peers remotos terão como destino o IP virtual. Desse modo, a máquina que estiver disponível como master responderá às solicitações. Caso essa máquina se torne indisponível, a outra assumirá automaticamente o IP virtual e passará a responder as solicitações.
No exemplo, utilizarei os IPs 172.0.0.200, 172.0.0.201 e virtual 172.0.0.202. Todas as requisições devem ir para 172.0.0.202.
A base de dados se comunica por background, diretamente pelos seus respectivos IPs. Qualquer operação que seja feita em uma, imediatamente é replicada para a outra e vice-versa. É possível adicionar slaves também para possuir um backup extra, mas não fará parte deste artigo.
Os servidores envolvidos serão nomeados aqui (e preferencialmente utilize a mesma denominação em seu ambiente) como node01 e node02.
* Configurando o heartbeat
Os arquivos de configuração do serviço heartbeat ficam em /etc/ha.d. Alguns arquivos não existem, mas basta criá-los com o respectivo conteúdo.
/etc/ha.d/authkeys
Seu conteúdo:

auth 2
2 sha1 test-ha
/etc/ha.d/haresources
Nesse arquivo especifica-se o nome do servidor master seguido do IP virtual e do script de reload das variáveis de ambiente de trabalho necessárias de se recarregar. Esse script pode ter qualquer nome e deve ficar em /etc/init.d/ como um serviço. Seu conteúdo será explanado mais adiante.

node01 172.0.0.202 reload_environment.sh
/etc/ha.d/ha.cf
Esse arquivo contém as pré-definições do funcionamento do heartbeat.

#arquivo de debug
debugfile /var/log/ha-debug
#arquivo de log
logfile /var/log/ha-log
#nivel de log
logfacility local0
#tempo de tolerância
keepalive 2
#tempo para considerar como morto
deadtime 10
#tempo de warning
warntime 10
#diretiva utilizada em tempo de inicialização do heartbeat
initdead 30
#...
udpport 694
#unicast respectivo ao host oposto; 201 no node01, 200 no node02
ucast eth0 172.0.0.201
# O auto_failback retoma como master automaticamente quando se encontra em condições de fazê-lo. Deixá-lo como
#off fará com que retorne somente se o host que assumir como master se perca.
auto_failback off
# node nodename ... -- must match uname -n
node node01 node02
#um host para ping. No caso, o gateway da rede de exemplo.
ping 172.0.0.254

#opcional
respawn root /usr/lib/heartbeat/ipfail
apiauth ipfail gid=root uid=root
apiauth default gid=root
apiauth cl_status gid=root

/etc/hostname
O nome curto deve aparecer como node01 ou node02. Constate isso com hostname -s.

/etc/hosts
Não experimentei resolver os nomes de outra maneira, então deixo a recomendação de incluir os IPs reais dos hosts no arquivo de hosts:

172.0.0.200 node01
172.0.0.201 node02
/etc/network/interfaces
Configure apenas os IPs reais em ambos os hosts. Por exemplo:

iface eth0 inet static
address 172.0.0.200
netmask 255.255.255.0
network 172.0.0.0
broadcast 172.0.0.255
gateway 172.0.0.254
/etc/apache2/ports.conf
Nesse arquivo deve-se especificar o IP virtual, tal como abaixo:

NameVirtualHost 172.0.0.202:80
Listen 80

# SSL name based virtual hosts are not yet supported, therefore no
# NameVirtualHost statement here
Listen 443
/etc/init.d/reload_environment.sh
Se preferir, renomeie o arquivo a contento, lembrando que se alterado, não esqueça de mudar o nome também no arquivo haresources.
O conteúdo desse arquivo é para exemplo. No caso, estou fazendo HA para o Asterisk:

#! /bin/sh
# /etc/init.d/reload_environment.sh
#
# Some things that run always
touch /var/lock/reload_environment.sh
# Carry out specific functions when asked to by the system
case “$1″ in
start)
#para o asterisk
/usr/sbin/asterisk -rx “restart now”
#matar o script XXX
$(which kill) -9 $(ps ax|egrep ‘XXX.py’|egrep -v ‘grep’|awk ‘{print $1}’) 2>/dev/null
exit 0
;;
stop)
echo “Nothing to do”
;;
*)
echo “Usage: /etc/init.d/blah {start|stop}”
exit 1
;;
esac
exit 0
Adeque-o para sua necessidade.
Iniciando o HeartBeat
Feitas essas configurações, o serviço pode ser iniciado:
/etc/init.d/heartbeat start
Se tiver algum erro, verifique suas configurações, os logs e finalmente faça uma busca no google. Se tudo estiver perdido, escreva o problema nos comentários e __se__ realmente for algo que julgar válido, escrevo a resposta.
Em alguns momentos o IP virtual deverá subir na interface eth0 (que foi a utilizada nesse exemplo) do node01. Isso representa o sucesso da operação.
Se desejar fazer um teste com o HA, insira em /var/www um arquivo html com o nome do host. Com ambos os hosts em um mesmo switch, a partir de um client acesse o IP virtual e veja o nome de host que aparece; faça refreshs a vontade. Enquanto isso, peça a alguém que remova o cabo do node que estiver sendo exibido durante seus refreshs. O IP virtual subirá no outro node.
No arquivo ha.cf você poderá então mudar o tempo de vida conforme desejado, para mais ou para menos, mas tudo dependerá da aplicação que pretende rodar em seu ambiente. Fatores como qualidade de rede física e lógica também podem influenciar.
Configurando o MySQL para Master-to-Master
O MySQL é impressionante por sua facilidade de configuração. Além de estar toda concentrada no arquivo /etc/mysql/my.cnf, não é necessário instalar nenhum pacote adicional para a tarefa. Não estou certo de que posso ser claro na explicação dos parâmetros, mas seguramente é uma configuração funcional. Seu conteúdo:

#padrao
[client]
port = 3306
socket = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
nice = 0
[mysqld]
#
# * Basic Settings
#
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
language = /usr/share/mysql/english
skip-external-locking
#
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address = 127.0.0.1
#
# * Fine Tuning
#
key_buffer = 16M
max_allowed_packet = 16M
thread_stack = 128K
thread_cache_size = 8
# This replaces the startup script and checks MyISAM tables if needed
# the first time they are touched
myisam-recover = BACKUP
#max_connections = 100
#table_cache = 64
#thread_concurrency = 10
#
# * Query Cache Configuration
#
query_cache_limit = 1M
query_cache_size = 16M
#
# * Logging and Replication
#
# Both location gets rotated by the cronjob.
# Be aware that this log type is a performance killer.
#log = /var/log/mysql/mysql.log
#
# Error logging goes to syslog. This is a Debian improvement :)
#
# Here you can see queries with especially long duration
#log_slow_queries = /var/log/mysql/mysql-slow.log
#long_query_time = 2
#log-queries-not-using-indexes
#
# The following can be used as easy to replay backup logs or for replication.
# note: if you are setting up a replication slave, see README.Debian about
# other settings you may need to change.
#configuracoes para o servico
#no node02 utilize 2 e no node01 utilize 1 no server-id
server-id = 2
replicate-same-server-id= 0
auto-increment-increment= 2
auto-increment-offset = 2
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size = 500M
#bases que nao serao replicadas devem ser explicitamente ignoradas
binlog_ignore_db = mysql
#bases a replicar
binlog_do_db = asterisk
replicate-do-db = asterisk
binlog_do_db = base_2
replicate-do-db = base_2
binlog_do_db = base_3
replicate-do-db = base_3
binlog_do_db = base_n
replicate-do-db = base_n
#o master para o nodeX é o outro node
master-host = 172.0.0.201
#sugestao: replicar com o mesmo usuario e senha para facilitar a configuracao
master-user = replicador
master-password = escravo
master-connect-retry = 60
#
# * BerkeleyDB
#
# Using BerkeleyDB is now discouraged as its support will cease in 5.1.12.
skip-bdb
[mysqldump]
quick
quote-names
max_allowed_packet = 16M
[mysql]
#no-auto-rehash # faster start of mysql but no tab completition
[isamchk]
key_buffer = 16M

#
# * IMPORTANT: Additional settings that can override those from this file!
# The files must end with '.cnf', otherwise they'll be ignored.
#
!includedir /etc/mysql/conf.d/

Terminada a configuração do my.cnf, deve-se ainda executar a permissão para o usuário de replicação. Algo como isto funciona bem:

mysql> grant all privileges on *.* to replicador@'%' identified by 'escravo';
Pode ser necessário (porque tenho certeza de que funcionou sem, mas já tive que fazê-lo) utilizar um comando para levantar o slave:

mysql> start slave;

A partir de então, pode-se testar fazendo um insert em qualquer um dos nodes e verificando sua inserção no outro e vice-versa. Alguns problemas podem ser resolvidos a partir desses documentos:
http://dev.mysql.com/doc/refman/4.1/pt/replication-problems.html
http://dev.mysql.com/doc/refman/4.1/pt/replication-faq.html
Alguns comandos também podem auxiliar em diagnósticos e informações, como:

show slave status\G;
show master status\G;
show processlist;
O comando show slave status\G deverá retornar em seu primeiro campo a informação:

Slave_IO_State: Waiting for master to send event
E mais abaixo, outras duas informações essenciais:

Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Infelizmente é inviável explicar todos os parâmetros, todos os comandos diagnósticos, ações a tomar em casos de falhas etc, mas ao menos você poderá iniciar um projeto de replicação com HA a partir de um ambiente funcional e os avanços dependerão apenas de você. ;-)