helio.loureiro.eng.br
  • Home
  • Unix
  • Linux
  • Blog
  • Python
  • Programação
  • Tudo
  • Suécia
  1. You are here:  
  2. Home

Os artigos mais lidos de 2025

  • Configurando o grafana alloy pra monitorar VMs
  • Configurando traefik com ssh
  • Configurando o teclado Keychron C3 no Linux
  • Parâmetros de compilação pra Go!
  • Script pra verificar e atualizar os certificados dos domínios com letsencrypt

Home

Uma introdução sobre voz

Details
Written by: Helio Loureiro
Category: Pré-pago
Published: November 15, 2011
Hits: 8489

Tudo tem um início.  Para fazer o restante dos artigos inteligíveis, é preciso explicar um pouco de telefonia, mais precisamente da transmissão da telefonia em forma de dados.  Ou na verdade como a voz vira dados.

Na figura abaixo é mostrado um exemplo, bem simplificado e limitado, de chamadas telefônicas em sistemas que chamamos de "rede fixa".  Antes da chegada dos celulares, era o que conheciamos como telefonia.




O telefones se comunicam com as "centrais telefônicas", ou "centrais de comutação", que pegam o conteúdo de voz do assinante e enviam para o destino.  As centrais telefônicas fazem também o roteamento para o telefone de destino.

Não entrando muito sobre os detalhes do antigo sistema de telefonia, mas comentando da parte que realmente interessa, basta saber que a comunicação entre os telefones e a central telefônica se faz de forma analógica.  Uma vez a voz chegando na mesma, essa é digitalizada e trocada entre as outras centrais totalmente digital.  Ao chegar na central do telefone destino, essa voz digitalizada é convertida em formato analógico novamente.

Os primeiros celulares usavam algo muito parecido, inclusive enviado o conteúdo da chamada (voz) em forma analógica, o que permitia a "escuta" da mesma.  Apesar da tecnlogia CDMA naquela época já permitir chamadas digitais, no Brasil adotou-se o formato analógico provavelmente pelo alto custo dos aparelhos celulares digitais.  Já no GSM, a comunicação é completamente digital.  Qualquer tecnologia a partir do GSM tem-se a chamada telefônica completamente digital.  O responsável pela digitalização é o próprio celular.

Não importando muito para explicar a rede de telefonia pré-paga, mas como curiosidade, existem dois modos de digitalização de voz: a lei-A e a lei-µ  (lei-mi).  Nos EUA é usada a lei-A e aqui no Brasil, a lei-µ.

Ambas as leis designam com quantos bits será digitalizada a voz.  Não em relação à quantidade, mas em relação à inclinação da curva de frequência da voz.  Falando de forma mais simplificada, quando a voz é mais grave, usa-se menos bits, quando é mais aguda, mais bits.  Onde e quando usar esses bits é o que é mapeado pelas tais leis.  Em geral ao se trocar os codecs de voz e misturar as leis, de um lado se ouve a tal "voz de pato", e do outro, a voz do Darth Vader.

Como o nosso espectro de voz, ou faixa de frequência da voz, varia entre 300 Hz e 4KHz, usa-se uma amostragem de 8 KHz para ser digitalizada.  Essa digitalização é feita por meio de pulsos, os chamados PCM, Pulse Coded Modulation, que "medem" a intensidade da voz e a digitalizam.

Essa voz digitalizada é transmitida entre as centrais telefônicas dentro de um link de 2 Mbps no Brasil (os chamados links E1) e 1.5 Mbps nos EUA (links T1).  Vários links E1 são agregados em links maiores, como links STM-1 (155 Mbps) ou até como STM-4 ( 622 Mbps).  Apesar do mundo IP já adotar redes de maior velocidade, com links de 10 Gbps, as redes de telefonia em geral ficam dentro do 622 Mbps.

Essa comunicação entre centrais com esses links é o que veremos nos próximos artigos.

Falando de telecom

Details
Written by: Helio Loureiro
Category: Pré-pago
Published: November 09, 2011
Hits: 7330

Já faz algum tempo tenho pensado em falar um pouco mais sobre telecomunicações por aqui.  Não que eu entenda muito de telefonia, mas boa parte dos sistemas em que trabalho, em geral redes de pré-pago, são na verdade máquinas Unix rodando ou Linux ou Solaris e com bancos de dados MySQL ou Oracle.  Algo bem mais simples que muita gente imagina.

Quando comecei a trabalhar nessa área, também tinha uma idéia de que centrais e serviços telefônicos rodavam em sistemas complexos e dedicados, com algum tipo de linguagem alienígena e que não seriam inteligíveis pelos outros seres humanos.  Em parte eu estava certo.  Existem as centrais de comutação, ou centrais telefônicas, que realmente são algo nesse nível.  Mas existem sim sistemas mais simples (pra nós, humanos) que rodam o bom e velho Linux.  Claro que isso não significa que eles estão somente lá pra fazer backup usando o comando "tar".  Existem também aplicativos sobre os mesmos que suportem aplicações de telefonia, dentro dos requisitos definidos para tal (como tempo de resposta menor de 10 ms por exemplo).

Então espero de agora em diante colocar aqui um pouco de conhecimento sobre essa parte do sistema de telefonia, esclarecendo tanto a parte Unix quanto a parte de telefonia em si.

O dia em que sofri um ataque de DDoS

Details
Written by: Helio Loureiro
Category: Security
Published: July 30, 2011
Hits: 14071

Terror da Internet, os ataques de DDoS ficaram em evidência ultimamente por causa dos grupos AnonOps e LuzSec, que atacaram vários sites usando essa técnica.  

DDoS nada mais é que um ataque do tipo "estouro de boiada".  Utilizando máquinas contaminadas, usa-se um programa "administrador" que envia o agendamento do ataque, com data e horário, para uma máquina de destino.  No caso esse destino foi num dos servidores que administro.  Por acaso onde está o serviço do http://eri.cx entre outros.

O ataque não foi dirigido diretamente à mim, mas um dos sites hospedados lá (não, não foi pro eri.cx).  Durante um ataque DDoS, isso não importa muito pois tudo que está hospedado no servidor é afetado juntamente.  Ou os serviços tornam-se indisponíveis ou a máquina pode travar, o que não deveria acontecer.   Mas um ataque DDoS não abre brechas pra invasão ou algo do gênero.

Felizmente conseguimos mitigar o ataque e minizar seus efeitos com algumas medidas bem simples, que são descritos aqui.

Comecei a suspeitar que algo estava errado quando recebi a mensagem abaixo, da lista de discussão que é mantida nesse servidor.


From: Mail Delivery System <This email address is being protected from spambots. You need JavaScript enabled to view it.>
Date: 2011/7/1
Subject: Undelivered Mail Returned to Sender
To: This email address is being protected from spambots. You need JavaScript enabled to view it.


This is the mail system at host domain.server.com.

I'm sorry to have to inform you that your message could not
be delivered to one or more recipients. It's attached below.

For further assistance, please send mail to postmaster.

If you do so, please include this problem report. You can
delete your own text from the attached returned message.

                  The mail system

<This email address is being protected from spambots. You need JavaScript enabled to view it.>: Command time limit exceeded: "/var/lib/mailman/mail/mailman
   post list"

Imediatamente tentei conectar no servidor e... nada.  Página web igualmente fora do ar.  Achei que poderia ser algum problema de hardware ou mesmo da máquina virtual.  Entrei em contato com a pessoa que tinha o acesso console na máquina e pedi para verificar o que estava acontecendo.

Momentos depois recebi o contato da pessoa avisando que a máquina estava travada e não conseguia dar boot: ela voltava a travar instantes depois.  Nesse momento imaginei que podia ser um ataque.

Pedi então para dar boot em "single mode" e desabilitar acesso ao apache e mysql e tentar o boot normalmente.  Felizmente deu certo, pois ataques DDoS são fortes em volume, mas não muito inteligentes e refinados.

Utilizando o tcpdump, demos uma olhada no tipo de tráfego que chega e vimos que o ataque era direcionado prioritariamente à porta 80 (web):


17:57:01.220234 IP 115.214.63.123.2844 > server.80: Flags [.], ack 1863118067, win 65306, length 0
17:57:01.237522 IP 115.214.63.123.2844 > server.80: Flags [F.], seq 0, ack 1, win 65306, length 0
17:57:01.237582 IP server.80 > 115.214.63.123.2844: Flags [.], ack 1, win 6432, length 0
17:57:01.241481 IP 86.96.227.91.39237 > server.80: Flags [.], ack 1882163284, win 65535, length 0
17:57:01.241527 IP 86.96.227.91.39237 > server.80: Flags [P.], seq 0:285, ack 1, win 65535, length 285
17:57:01.241573 IP server.80 > 86.96.227.91.39237: Flags [.], ack 285, win 6432, length 0
17:57:01.269729 IP server.80 > 86.96.227.91.39237: Flags [.], seq 1:1461, ack 285, win 6432, length 1460
17:57:01.269774 IP server.80 > 86.96.227.91.39237: Flags [P.], seq 1461:2802, ack 285, win 6432, length 1341

Vários pedidos de início de conexão, sem resposta.

O efeito do ataque pode ser visto nesse gráfico, entre as semanas 26 e 27:

Olhando nos logs do apache, pudemos identificar qual o domínio que estava sob ataque.  Confirmamos com tcpdump também que boa parte do ataque buscava tal domínio pelo DNS, verificando o tráfego na porta 53.  Então tomei a primeira providência para mitigar o ataque: troquei os IPs do domínio no DNS para 127.0.0.1 (loopback).

Como anúncios de DNS demoram um pouco para serem propagados, essa solução começou a mostrar um pouco mais de eficiência somente depois de 1 hora.  Mas isso reduziu o ataque na ordem de 60%, ou até mais.

O DNS seria suficiente, mas o ataque poderia voltar buscando somente o IP e esse truque não iria ajudar muito.  Então comecei a olhar como estava configurado o servidor Apache, pois um servidor Unix (ou Linux no caso) não pode travar por conta de um ataque desses, a menos que tenha alguma configuração incorreta.

Como o gráfico mostra, o servidor Apache com suas configurações padrão não foi afetado pelo ataque.  Vários processos foram criados, mas finalizados em seguida.

Tentei então verificar como estava o sistema durante o ataque, para tentar identificar o que o ataque tinha ocasionado para travar.

Como os gráficos mostram, o ataque não causou nenhum uso excessivo de CPU ou do sistema.  Nada que pudesse levar a máquina ao travamento.  Mas o servidor travava com o serviço do Apache ligado.  Então resolvi olhar os parâmetros TCP do sistema.

Essa imagem me deu a noção clara que os recursos de redes estavam sendo usados em excesso.  Mas mesmo isso não deveria causar o travamento de servidor.

Então resolvi olhar como estava o firewall.

 

A imagem que ajudou a entender o problema foi a de ipconntrack.  Como o netfilter (iptables) utiliza um modo "stateful", cada pacote que entra no sistema ocupa uma posição de memória, para poder identificar (e liberar) as conexões restantes vindas dessa origem.  Como o sistema estava sob ataque, a posição era alocada, mas ficava parada aguardando o fim da conexão, que não acontecia.  Pra piorar a situação, o firewall alocava memória e recursos do sistema em kernel level, motivo pelo qual o sistema travava.

Para mitigar esse problema, alteramos os parâmetros de kernel com sysctl da seguinte forma:


net.netfilter.nf_conntrack_generic_timeout=5
net.netfilter.nf_conntrack_tcp_timeout_syn_sent=120
net.netfilter.nf_conntrack_tcp_timeout_syn_recv=10
net.netfilter.nf_conntrack_tcp_timeout_established=10
net.netfilter.nf_conntrack_tcp_timeout_fin_wait=5
net.netfilter.nf_conntrack_tcp_timeout_close_wait=3
net.netfilter.nf_conntrack_tcp_timeout_last_ack=10
net.netfilter.nf_conntrack_tcp_timeout_time_wait=5
net.netfilter.nf_conntrack_tcp_timeout_close=10
net.netfilter.nf_conntrack_tcp_timeout_max_retrans=300
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged=300
net.netfilter.nf_conntrack_tcp_loose=1
net.netfilter.nf_conntrack_tcp_be_liberal=0
net.netfilter.nf_conntrack_tcp_max_retrans=3
net.netfilter.nf_conntrack_udp_timeout=30
net.netfilter.nf_conntrack_udp_timeout_stream=180
net.netfilter.nf_conntrack_icmp_timeout=30
net.netfilter.nf_conntrack_icmpv6_timeout=30
net.netfilter.nf_conntrack_frag6_timeout=60
net.netfilter.nf_conntrack_frag6_low_thresh=196608
net.netfilter.nf_conntrack_frag6_high_thresh=262144
net.netfilter.nf_conntrack_acct=1
net.netfilter.nf_conntrack_events=1
net.netfilter.nf_conntrack_events_retry_timeout=15
net.netfilter.nf_conntrack_max=15876
net.netfilter.nf_conntrack_count=254
net.netfilter.nf_conntrack_buckets=4096
net.netfilter.nf_conntrack_checksum=1
net.netfilter.nf_conntrack_log_invalid=0
net.netfilter.nf_conntrack_expect_max=64
net.core.somaxconn=128
net.ipv4.netfilter.ip_conntrack_generic_timeout=5
net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_sent=120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_sent2=120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_recv=20
net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=10
net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait=5
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait=3
net.ipv4.netfilter.ip_conntrack_tcp_timeout_last_ack=10
net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait=5
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close=10
net.ipv4.netfilter.ip_conntrack_tcp_timeout_max_retrans=300
net.ipv4.netfilter.ip_conntrack_tcp_loose=1
net.ipv4.netfilter.ip_conntrack_tcp_be_liberal=0
net.ipv4.netfilter.ip_conntrack_tcp_max_retrans=3
net.ipv4.netfilter.ip_conntrack_udp_timeout=30
net.ipv4.netfilter.ip_conntrack_udp_timeout_stream=180
net.ipv4.netfilter.ip_conntrack_icmp_timeout=30
net.ipv4.netfilter.ip_conntrack_max=15876
net.ipv4.netfilter.ip_conntrack_count=254
net.ipv4.netfilter.ip_conntrack_buckets=4096
net.ipv4.netfilter.ip_conntrack_checksum=1
net.ipv4.netfilter.ip_conntrack_log_invalid=0
net.nf_conntrack_max=15876

O padrão de timeout para TCP é de 2 horas.  Alterei para alguns segundos.  Com isso pudemos reiniciar todos os serviços e mitigar totalmente o ataque, tendo a certeza que o servidor não travaria novamente.

Joomla atualizado

Details
Written by: Helio Loureiro
Category: Joomla
Published: July 30, 2011
Hits: 7557

Site atualizado para última versão estável, 1.5.23.  


Estava pensando em migrar para 1.7, mas acabei descobrindo pelo FAQ que a versão 1.5.x é uma LTS, Long Term Supported, ou suporte de long prazo.

Então vou continuar na geração 1.5.x por enquanto.

mycrontab

Details
Written by: Helio Loureiro
Category: Perl
Published: June 25, 2011
Hits: 12323

No sistema de agendamento de tarefas, o cron, existe a possibilidade de controle de acesso pelos arquivos /etc/cron.allow e  /etc/cron.deny.  Existindo somente o /etc/cron.allow no sistema, o usuário deverá ter seu login incluso nesse arquivo para fazer uso do cron.  Do contrário será brindando com uma mensagem semelhante a essa:

You (helio) are not allowed to use this program (crontab)
See crontab(1) for more information

Nos ambientes de telecomunicações, onde muitos dos sistemas operacionais em uso são Solaris, ex-Sun e agora da Oracle, esses vêm com essa forma de controle do cron habilitada por padrão.

Isso aumenta a segurança do sistema, mas também torna alguns trabalhos simples mais difíceis, principalmente quando é necessário ter agendamento de trabalho.  Claro que isso poderia ser resolvido com ajuda de um bom administrador de sistemas, o sysadmin, e conhecimento em Unix, mas isso parece estar se tornando cada vez mais raro nesses dias.

Então criei o mycrond, um daemon escrito em perl, para rodar periodicamente como o crond do sistema.  A sintaxe não é perfeita e tem alguns furos, como não aceitar "*/5" para tarefas a cada 5 intervalos, mas utiliza uma sintaxe que somente Solaris aceita como, por exemplo para tarefas agendadas para cada 5 minutos, utilizar "0,5,10,15,20,25,30,35,40,45,50,55".  O restante é parecido com o uso normal da crontab.

Não é 100% perfeito, pois acabei deixando a análise de sintaxe incompleta para todos os valores de data, mas funciona perfeitamente como daemon, não morrendo ao sair do sistema, e é capaz de analisar e rodar formas mais simples da crontab.

O conteúdo do programa é o seguinte:


#! /usr/bin/perl
# $Id: mycrond 29 2011-06-22 20:42:35Z helio $
# Date: Wed Jun 22 12:37:00 BRT 2011
# Author: Helio Loureiro
# Description: an user level alternative to system crond.
#


my $ROOTDIR = "/home/helio/AUDIT";

exit(0); # Edit $ROOTDIR settings and comment this exit.

my $CRONFILE = $ROOTDIR."/etc/crontab";
my $PIDFILE = $ROOTDIR."/var/run/mycrond.pid";

$SIG{CHLD} = 'IGNORE'; #avoid zombies, or they can eat your brain

### Starting in daemon mode
&Daemonize();


sub CheckDir() {
	my $dir = $_[0];
	if ( ! -d "$dir" ) {
		system("mkdir -p $dir");
	}
}

sub ParseCronLine() {
	my $cron = $_[0];
	chomp($cron);

my @params = split(/ /, $cron); my $r_minute = $params[0]; my $r_hour = $params[1]; my $r_day = $params[2]; my $r_month = $params[3]; my $r_dow = $params[4]; my $command = $params[5]; for (my $x = 6; $x < @params; $x++) { $command .= " ".$params[$x]; } my ($second, $minute, $hour, $dayOfMonth, $month, $year, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime(); $year += 1900; $month++; ### Day of week requires some more work if ($r_dow != '*') { if ($r_dow != $dayOfWeek) { return 0; } } if ($r_month != '*') { $run_status = 0; @months = split(/,/, $r_month); foreach $m (@months) { if ($m == $month) { $run_status++ ; } } return if (! $run_status); } if ($r_day != '*') { $run_status = 0; @days = split(/,/, $r_day); foreach $d (@days) { if ($d == $dayOfMonth) { $run_status++ ; } } return if (! $run_status); } if ($r_minute != '*') { $run_status = 0; @minutes = split(/,/, $r_minute); foreach $m (@minutes) { if ($m == $minute) { $run_status++ ; } } return if (! $run_status); } if ($r_hour != '*') { $run_status = 0; @hours = split(/,/, $r_hour); foreach $h (@hours) { if ($h == $hour) { $run_status++ ; } } return if (! $run_status); } # If it is ok, then run your command if (defined($DEBUG)){ print "Running: $command\n"; } exec "$command" or exit(1); exit(0); } sub Run() { ### Saving process PID my $pid = getppid(); $pid++; &CheckDir($ROOTDIR."/var/run"); open(PID, ">$PIDFILE") or die "Impossible to create $PIDFILE: $!\n"; print PID $pid."\n"; close(PID); open(FD, $CRONFILE) or die "Impossible to read crontab: $!\n"; my @cronlines = ; close(FD); my @PIDS; while (1) { foreach my $line (@cronlines) { next if ($line =~ m/^#/); my $runpid = fork(); if ($runpid == 0) { &ParseCronLine($line); exit(0); } if ($runpid > 0) { push(@PIDS, $runpid); } } sleep(60); } } sub Daemonize() { # Change to / chdir("/"); my $pid = fork(); #first fork if ($pid > 0) { #Parent exists exit(0); } if ($pid == 0) { # Son forks again my $pid2 = fork(); if ($pid2 > 0) { exit(0); } if ($pid2 == 0) { &Run(); } } }

Melhorando o desempenho do Linux

Details
Written by: Helio Loureiro
Category: Linux
Published: June 17, 2011
Hits: 12474

Não tem nada mais chato que sistema lento.  Atualmente não dá pra aguentar um sistema que fica cortando a música que se está ouvindo só porque o Firefox consumiu 2 GB de memória, a máquina virtual no VirtualBox tá com mais 2 GB alocados e ainda tá compilando um kernel.

Pois é exatamente o que tem acontecido e muito em Linux.  Não sei de outras distribuições, mas especificamente em Ubuntu.

Como os patches de tempo real foram incluídos na árvore principal do kernel já faz algum tempo, isso deveria estar bem mais amenizado.  Fui em busca de informação sobre como ativar tal função e encontrei o comando "chrt" (change realtime talvez).

Lendo o manual do chrt, é possível ver uma gama de opções sem muitas explicações, nem comparativos de resultados.  Isso não ajuda muito na ampla adoção do mesmo.  Eu acabei fazendo alguns experimentos tanto em Intel 32 bits quanto em 64 e consegui um resultado supreendemente bom e bem fácil.  Apenas adicionei prioridade de tempo real ao processo init.


chrt -r -p 1 1

Esse comando adiciona a política de escalonamento SCHED_RR ao processo ID 1 (init) com prioridade 1. 

A política default do init é SCHED_OTHER, que de acordo com manual - SCHED_SETSCHEDULER(2) - significa:

SCHED_OTHER   política padrão de round-robin baseado em compartilhamento de tempo

SCHED_RR      política round-robin

Olhando mais a fundo o manual do escalonador, é possível ver que somente o SCHED_RR ativa a funcionalidade de tempo real.

Então basta adicionar esse comando no "/etc/rc.local" do Ubuntu/Debian para ter um sistema sem problemas de "travadinhas" quando sobrecarregado.  Eu fiquei mesmo surpreso em como foi fácil melhorar o desempenho do sistema e como isso não é incluído por padrão nos sistemas.

Limpando os SPAMs do eri.cx

Details
Written by: Helio Loureiro
Category: Python
Published: April 15, 2011
Hits: 8346

Ultimamente, monitorando meu encurtador eri.cx, notei que o mesmo estava recebendo muitos links sem nenhum clique.  Olhando mais de perto pude ver que os mesmos eram relacionados à SPAM/vírus.  Alguns robôs de Internet, ou mesmo máquinas zumbis, que adicionam esses links suspeitos.

Um dos agressores foi claramente o IP 213.5.66.88.   Resolvi o problema adicionando-o em duas regras de firewall bem simples:


iptables -A INPUT -p tcp --dport 80 -s 213.5.66.88 -j LOG
iptables -A INPUT -p tcp --dport 80 -s 213.5.66.88 -j REJECT

Criei uma tabela chamada quarantine e migrei as entradas do banco para lá, manualmente.  E também notifiquei os administradores do site sobre o ocorrido, enviando mail para abuse@site e security@site.

Achei que a solução era perfeita e que tinha resolvido meus problemas.  Essa semana dei uma  olhada novamente na base do encurtador.  De 900 links inseridos, uns 350 eram desse tipo (malditos SPAMMERS!).

Chorei minhas mágoas no Twitter, bem no estilo "reclamar no Twitter muito", e recebi algumas sugestões para evitar esses ataques.  Mesmo meu guru mental para idéias técno-copiadas, Eduardo Maçan, que administra o encurtador miud.in -- do qual chupinhei várias coisas, sugeriu uma verificação do encurtador via web através do refereer antes de encurtar qualquer link.

Pensei em fazer isso, mas tornaria impossível notificar os administradores dos sites afetados que suas máquinas estão comprometidas, e fora o problema de performance que poderia ocorrer ainda mais que o hosting que contratei tem somente 64 MB de RAM e CPU compartilhada num ambiente Xen.  Então resolvi mudar a abordagem e simplesmente deixar as máquinas zumbis adicionarem as URLs "do mal".  Mas uma vez por dia eu rodo um programa em Python que faz a busca de padrões desses vírus/scammers, move pra base do quarentine (assim ninguém clicará nos mesmos) mantendo as mesmas entradas, e comunica ao mails abuse@domínio e security@domínio sobre o ataque.

Então segue o script que fiz para esse fim.


#! /usr/bin/python
# -*- coding: utf-8 -*-
# $Id:$ 

# COPYRIGHT
#
#    Copyright (c) 2011 by Helio A. L. Loureiro
#
#    Permission to use, copy, modify, distribute, and sell this software
#    for any purpose is hereby granted without fee, provided that the above
#    copyright notice appear in all copies and that both that copyright
#    notice and this permission notice appear in supporting documentation.
#    No representations are made about the suitability of this software for
#    any purpose. It is provided "as is" without express or implied
#    warranty.

### main code
from os import popen
from re import sub, search
import smtplib
from time import ctime,strftime


mysqldb = "eri.cx"
mysqluser = "eri.cx"
mysqlpasswd = "xc.ire"
log_dir = "/var/log"

# Enviando mensagens
def Log(msg):
	log_file = log_dir + "/report-sql-spam-" + \
		strftime("%Y-%m") + ".log"
	msg = msg.rstrip("\n")
	msg = "[ " + ctime() + " ] " + msg + "\n"
	log = open(log_file, "a")
	log.write(msg)
	log.close()

# Formatando o texto do mysql
def GenSQLCommand(sql):
	cmd = "mysql -u %s -p\'%s\' %s << EOF" % (mysqluser, mysqlpasswd, mysqldb)
	cmd = cmd + "\n" + sql + "\n" + "EOF\n"
	
	return cmd
	
def RetrieveMySQL():
	sql = """SELECT 
	yourls_url.keyword,
	yourls_url.url, 
	yourls_url.title,
	yourls_url.timestamp,
	yourls_url.ip,
	yourls_url.clicks
	FROM yourls_url WHERE  
	yourls_url.title like '%dingo.ucsf.edu%' OR
	yourls_url.title like '%cssa.grad.wustl.edu%' OR
	yourls_url.title like '%wiki.usfca.edu%' OR
	yourls_url.title like '%pantherfile.uwm.edu%' OR
	yourls_url.title like '%csci.marietta.edu%' OR
	yourls_url.title like '%webfiles.uci.edu%' OR
	yourls_url.title like '%Ultram %' OR
	yourls_url.title like '%Cialis%' OR
	yourls_url.title like '%Tramadol%' OR
	yourls_url.title like '%xanax%' OR
	yourls_url.title like '%viagra%' ORDER BY yourls_url.keyword;
	"""
	cmd = GenSQLCommand(sql)
	
	sql_result = popen(cmd).readlines()
	sql_result = sql_result[1:]

	return sql_result
	
def InsertQuarantine(db_lines):
	sql = """INSERT INTO yourls_quarantine 
	(keyword, url, title,timestamp,ip,clicks) VALUES """
	for e in db_lines:
		sql = sql + "\n( "
		e = e.rstrip("\n")
		for d in e.split("\t"):
			sql = sql + "\'" + d + "\', "
		sql = sql[:-2] + " ),"
	sql = sql[:-1]
	cmd = GenSQLCommand(sql)
	popen(cmd).read()
	
def DeleteEntries(keywords):
	sql = """DELETE FROM yourls_url USING yourls_url WHERE """ 
	sql = sql + "keyword = \"%s\"" % keywords[0]
	for k in range(1, len(keywords)):
		sql = sql + "OR keyword = \"%s\"" % keywords[k]
		
	cmd = GenSQLCommand(sql)
	popen(cmd).read()
	
def GetDomain(url):
	url = sub("^http://", "", url)
	url = sub("^https://", "", url)
	url = sub("/.*", "", url)
	
	return url

def SendMail(rcpts, msg):
	fromaddr = "helio" + "@" + "domain.com"
	mail = smtplib.SMTP('localhost')
	mail.sendmail(fromaddr, rcpts, msg)
	mail.quit()

def Main():
	spam_list = RetrieveMySQL()
	
	ATTACKED = {}
	KEYWORDS = []
	for spam in spam_list:
		Log(sub("\t", ",", spam))
		keyword, url, title, timestamp, ip, clicks = spam.split("\t")
		domain = GetDomain(url)
		KEYWORDS.append(keyword)
		try:
			ATTACKED[domain].append(spam)
		except KeyError:
			ATTACKED[domain] = [spam]
			
	for poor_soul in ATTACKED:
		rcpts = ["abuse@" + poor_soul, 
		"root@" + poor_soul,
		"security@" + poor_soul]
		msg = """From: Helio Loureiro <""" + "helio" + "@" + "domain.com" + """>
To: abuse@%s, root@%s, security@%s
Subject: Service abused from your domain

Hi,
		
I'm the sysadmin from domain eri.cx, a little poor shortener.
Recently I noticed some attacks (SPAM url shorteneds) coming from
your site.

Here follows the logs:

#timestamp|ip|title|url
""" % (poor_soul, poor_soul, poor_soul)
		for spam in ATTACKED[poor_soul]:
			keyword, url, title, timestamp, ip, clicks = spam.split("\t")
			msg = msg + "%s|%s|%s|%s\n" % (timestamp, ip, title, url)
		#print poor_soul, "=>", ATTACKED[poor_soul]
		
		msg = msg + """
It seems your server was compromised.  So please take actions accordingly.

This is an automatic mail report.  My apologizes if it sent unwanted 
information.

Best Regards,
Helio Loureiro
http://eri.cx
"""
		#print msg
		SendMail(rcpts, msg)
		InsertQuarantine(spam_list)
		DeleteEntries(KEYWORDS)

if __name__ == '__main__':
	Log("== Starting... == ")
	Main()
	Log("## Finishing... ## ")

Enviando IP por mail com Python

Details
Written by: Helio Loureiro
Category: Python
Published: April 15, 2011
Hits: 9060

De tanto @eduardomacan e @ale_borba reclamarem dos meus posts repetitivos no twitter, aqui segue então mais uma dica com Python pra dar um refresco na repetitividade.

Tenho algumas máquina velhas na empresa, umas Sun Ultra 10, que uso pra compilar e testar código.  Uma com Solaris 10 e outra com o 9 (ambas fazendo jus ao nome Slowlaris).  Elas tinham IP fixo e ficavam num ambiente de laboratório, mas recentemente resolveram tentar *cobrar* o espaço físico e a energia elétrica, além da "administração do sistema".   Resolvi então pegar ambas e as colocar próximas de mim, no ambiente de escritório.

O maior problema disso é que o ambiente disponibiliza IP somente por DHCP.  Então como resolver a questão de saber qual o IP da máquina para fazer conexão via SSH, uma vez que o mesmo pode mudar a cada novo boot (e realmente muda por aqui)?

Como sempre, Python veio para salvar o dia.  Uso o programa abaixo para enviar por mail qual o IP do servidor.  É feito para Solaris, por isso algumas definições são específicas, como o uso da interface "hme0" e a localização do comando "ifconfig".  Mas tudo pode ser adaptado facilmente para outra plataforma.


#! /usr/bin/python

import smtplib
from os import popen
from string import split
from re import search, sub
from socket import gethostname
from time import ctime

interface = "hme0"
ifconfig = "/usr/sbin/ifconfig"
mailserver = "mail.internal.domain.com"

def SendMail(rcpts, ip):
	fromaddr = "This email address is being protected from spambots. You need JavaScript enabled to view it."
	subject = "== Location update: %s (%s) == " % (ip, gethostname())
	msg = "From: %s\nTo: " % fromaddr
	for n in rcpts:
		msg = msg + n + ","
	msg = msg[:-1] + "\nSubject: %s" % subject
	msg = msg + """

Location update from host %s: %s

%s
-- Powered by Python --
""" % (gethostname(), ip, ctime())
	mail = smtplib.SMTP(mailserver)
	#mail.set_debuglevel(1)
	print "Sending:", msg
	mail.sendmail(fromaddr, rcpts, msg)
	mail.quit()
	
def GetIP():
	for line in popen(ifconfig + " " + interface).readlines():
		#print line
		if search("inet", line):
			line = line[:-1]
			line = sub(".*inet ", "", line)
			line = sub(" .*", "", line)
			
			return line
			
		
	
def Main():
	ip = GetIP()
	rcpts = [ "This email address is being protected from spambots. You need JavaScript enabled to view it.",
		"This email address is being protected from spambots. You need JavaScript enabled to view it.",
		"This email address is being protected from spambots. You need JavaScript enabled to view it."
		]
	SendMail(rcpts, ip)

if __name__ == '__main__':
	Main()

Em seguida apenas adicionei o mesmo dentro do "/etc/init.d" e fiz o link em "/etc/rc3.d".

Escrevendo uma aplicação com python-twitter (parte 3 - final)

Details
Written by: Helio Loureiro
Category: Python
Published: April 03, 2011
Hits: 9540

Após as partes 1 e 2 sobre como escrever uma aplicação com python-twitter, onde foi visto como instalar o pacote python-twitter ou via fonte ou via pacote Debian, e como registrar sua aplicação no Twitter, finalmente chegamos ao final: escrevendo a aplicação em python.

O objetivo de toda essa série de descrição de como utilizar uma aplicação de twitter-python era mostrar como fiz um script para automatizar o #FF, enviado às sexta-feiras.  

Inicialmente escrevi o seguinte script que fazia exatamente o #FF para todos os meus seguidores:


#! /usr/bin/python
import twitter

# App python-tweeter
# https://dev.twitter.com/apps/815176
api = twitter.Api(
    consumer_key = 'xxxxxxx',
    consumer_secret = 'yyyyyyyy',
    access_token_key = 'zzzzzzz',
    access_token_secret = 'zzzzzzzz'
    )
FOLLOWERS = []
users = api.GetFollowers()
for u in users:
        sc_name = str(u.screen_name)
        FOLLOWERS.append(['@' + sc_name])

SIZE = 140
MSG = "#FF"
i = 1
api.PostUpdate("Automated python-twitter-#FF mode=on")
for uid in FOLLOWERS:
    print i, uid
    if ( len(MSG + " " + uid) > SIZE):
        print MSG
        api.PostUpdate(MSG)
        sleep(60)
        MSG = "#FF " + uid
    else:
        MSG += " " + uid
    i += 1

print MSG
api.PostUpdate(MSG)

Esse pequeno script mostrou uma limitação do uso do python-twitter 0.8.1: a lista de usuários era buscada somente com os último 100 seguidores.  O demais não retornavam.  Eu corrigi esse comportamento e lançei o "0.8.2", mas na verdade ainda não foi aprovado.

Basicamente o módulo twitter, ou classe, permite buscar todos os seguidores com o método GetFollowers().  Cada seguidor retornado volta como uma matriz (array) de seguidores, com seus parâmetros como atributo do objeto retornado.  Então como exemplo um dos meus seguidores:


>>> print users[0]
{"description": "Expert in software development, photographer, open networker, 
 writer, handyman, old DJ+radiohost, idealist, creative, easily inspired, 
 very independent, spunky", 
"favourites_count": 44, 
"followers_count": 27800, 
"friends_count": 30580, 
"geo_enabled": true, 
"id": 5377742, 
"location": "Copenhagen, Denmark", 
"name": "J\u00f8rgen Larsen", 
"profile_background_color": "000000", 
"profile_background_tile": true, 
"profile_image_url": 
 "http://a0.twimg.com/profile_images/728474665/J_rgen_Larsen_org_nobackground_small_128x128_normal.png", 
"profile_link_color": "000000", 
"profile_sidebar_fill_color": 
 "http://a1.twimg.com/profile_background_images/78764489/background3.jpg", 
"profile_text_color": "333333", 
"protected": false, 
"screen_name": "porcupinex", 
"status": {"created_at": "Fri Apr 01 06:36:13 +0000 2011", 
    "favorited": false, 
    "id": 53707220474544128, 
    "source": "HootSuite", 
    "text": "RT @renejsum: RT @tinybuddha \"We must accept finite disappointment, but never lose infinite hope.\"
        ~Martin Luther King Jr. (Til @jesperdahl)", 
    "truncated": false}, 
"statuses_count": 1500, 
"time_zone": "Copenhagen", 
"url": "http://on.fb.me/porcupinex", 
"utc_offset": 3600}

chamando cada user[] como follower (for follower in user:), é possível verificar seus atributos usando algo como follower.url, follow.screen_name e assim por diante.  Em geral o que se vê no twitter é no formato @helioloureiro, que é uma referência ao atributo screen_name, esse sem o "@" originalmente.

Para publicar algo usando python-twitter, basta utilizar o método PostUpdate()


api.PostUpdate("Testando via twitter...")

Tudo isso já estaria de bom tamanho se não fossem duas coisas: o @ale_borba reclamou do flood de #FFs e realmente eu não queria mandar #FF para todo mundo, mas somente para quem realmente participa no Twitter.  Como eu tenho a política de seguir de volta quem me segue, tem muitos "robôs" que eu gostaria de evitar.  Então resolvi criar um grupo "amigos" e buscar somente os seguidores desse grupo:

 

Acabei escrevendo a busca de seguidores por grupo sem utlizar a API do python-twitter, com urllib2 e json:


lstname = 'Amigos'
AMIGOS = []
NOMES = {}
FOLLOWERS = {}
parameters = {}
parameters['cursor'] = -1
ic = 0
while (parameters['cursor'] != 0):
    url = '%s/%s/%s/members.json' % (api.base_url, user,lstname)
    
    json = api._FetchUrl(url, parameters=parameters)
    data = simplejson.loads(json)
    for u in data['users']:
        AMIGOS.append(u)
    parameters['cursor'] = int(data['next_cursor_str'])

    if (ic == 10): break
    ic += 1

Para evitar ainda um flood de mensagens (ou trollada), inclui ainda um randrange() pra criar uma aleatoriedade nos #FFs.  Comecei com 50%, mas aumentei pra 33% (1/3).  Também adicionei um belo sleep() para evitar o envio massivo de #FF, tornando um pouco mais suave as indicações.

O código final que utilizo atualmente é esse:


#! /usr/bin/python
import twitter
from time import sleep
from sys import exit
import simplejson
from random import randrange

# App python-tweeter
# https://dev.twitter.com/apps/815176
api = twitter.Api(
    consumer_key = 'xxxxxx',
    consumer_secret = 'yyyyyy',
    access_token_key = 'zzzzzzz',
    access_token_secret = 'kkkkkkkk'
    )

user = 'helioloureiro'
lstname = 'Amigos'
AMIGOS = []
NOMES = {}
FOLLOWERS = {}
parameters = {}
parameters['cursor'] = -1
ic = 0
while (parameters['cursor'] != 0):
    url = '%s/%s/%s/members.json' % (api.base_url, user,lstname)
    
    json = api._FetchUrl(url, parameters=parameters)
    data = simplejson.loads(json)
    for u in data['users']:
        AMIGOS.append(u)
    parameters['cursor'] = int(data['next_cursor_str'])

    if (ic == 10): break
    ic += 1

for a in AMIGOS:
    NOMES[a['screen_name']] = 1

users = api.GetFollowers()
for u in users:
    status = ""
    try:
        if (NOMES[u.screen_name] == 1):
            FOLLOWERS['@' + u.screen_name] = 1
            status = "OK"
    except:
        status = "NOT"

    print u.screen_name, status
    
SIZE = 140
MSG = "#FF"
i = 1
api.PostUpdates("Automated python-twitter-#FF mode=on")
for uid in FOLLOWERS.keys():
    if not (randrange(0,2)):
        print "Nao foi:", uid
        continue
    print i, uid
    if ( len(MSG + " " + uid) > SIZE):
        print MSG
        api.PostUpdate(MSG)
        sleep(60)
        MSG = "#FF " + uid
    else:
        MSG += " " + uid
    i += 1

sleep(60)
print MSG
api.PostUpdate(MSG)

api.PostUpdates("Python Twitter #rockz!!!")
api.PostUpdates("Automated python-twitter-#FF mode=off")

Com esse princípio de uso do python-twitter já criei várias aplicações, inclusive para re-enviar minhas publicações daqui e ainda utilizando o eri.cx para encurtar os link.

Escrevendo uma aplicação com python-twitter (parte 2)

Details
Written by: Helio Loureiro
Category: Python
Published: March 30, 2011
Hits: 10355

Na primeira parte da série de artigos sobre como escrever uma aplicação em python-twitter, apresentei como baixar e instalar os pacotes necessários.  E ainda uma sugestão para utilizar uma versão a mais do python-twitter, 0.8.2.

Agora falta comentar como criar sua configuração no twitter e como utilizar as chaves para autenticação com python-oauth2.

Tendo uma conta aberta no twitter, a criação da aplicação se faz através do http://dev.twitter.com.

Clicando em "Your apps" se obtém acesso ao sistema de criação e gerenciamento de aplicações.

 

Para criar uma aplicação, caso ainda não tenha uma, clique em "Register a new app" no menu da direita.

O item "Aplication Name", que parece não ter relação nenhuma além de um nome pra se lembrar, é o que cria a referência de aplicativo utlizado para posts no Twitter (aparece na parte de baixo de um post).  Então um nome legal faz toda a diferença, tal qual batizar um filho.

Também percebi que não é possível utilizar nomes já existentes, tanto que tentei registrar o nome da minha aplicação como "python-twitter" e o sistema não permitiu, então utilizei "python-tweeter".

Crie o aplicativo como "cliente", pra evitar troca de chaves via web e  com permissão de "leitura e escrita".

Criada a sua aplicação, agora é hora de olhar a parte de credencias para seu uso com o módulo oauth2, ou melhor, python-oauth2.

Utilizando python-twitter, com oauth2 pra autenticação, cria-se um objeto de tweet como abaixo:

tweet = twitter.Api(
    consumer_key = '9zz1zuWjMy0aKm2d6HK20Q',
    consumer_secret = 'd5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    access_token_key = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
    access_token_secret = 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'
    )

A nomenclatura das informações necessárias são muito parecidas com as utilizadas pelo Twitter, facilitando a localização dos mesmos.  "Consumer Key" no Twitter refere-se à consumer_key no python-oauth2, "Consumer Secret" à consumer_secret, "Access Token (oauth_token)" à access_token_key e "Access Token Secret (oauth_token_secret)" à access_token_secret.

Após criado seu aplicativo, basta clicar no mesmo para ver tais dados.  Pelo que pude perceber, o ÄPI Key" e "Consumer Key" são os mesmos, mas os dados sobre seu aplicativo mostram primeiramente o "API Key".

Isso já completa parcialmente os dados necessários para autenticação.  Os dados seguintes referem-se às credenciais de usuário, que não utilizará a web para se autenticar, como foi selecionado anteriormente.  Nesse caso é possível encontrar tais dados no menu da direita, ainda dentro dos detalhes da aplicação.

Basta clicar em "My Access Token" para obter as informações necessárias.

Com essas informações em mãos, já é possível fazer um pequeno teste seguido de um "Hello World" no Twitter totalmente em Python:


#! /usr/bin/python
import twitter

tweet = twitter.Api(
    consumer_key = '9zz1zuWjMy0aKm2d6HK20Q',
    consumer_secret = 'd5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    access_token_key = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
    access_token_secret = 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'
    )

print tweet.VerifyCredentials()
print tweet.GetPublicTimeline()
tweet.PostUpdate("Hello World")

Escrevendo uma aplicação com python-twitter (parte 1)

Details
Written by: Helio Loureiro
Category: Python
Published: March 24, 2011
Hits: 11002

Depois que fiz uma aplicação em Python pra enviar #FF pras pessoas que sigo, recebi alguns pedidos pra mostra um HOWTO ou algo parecido. Como foi um procedimento bastante longo de se descrever, resolvi seguir o modelo "Jack estripador" e mandar em partes:python-twitter

  • Parte 1: pacotes Debian
  • Parte 2: configuração do Twitter
  • Parte 3: programa em Python

Pode parecer um certo exagero, mas é que precisei adaptar várias coisas tanto na parte 1 quanto na 3. Se eu descrever tudo de uma vez só, vai ficar muito, mas muuuuuuito longo.

Agora falando um pouco sobre python-twitter, esse é um módulo (ou classe) pra conectar no Twitter (duh!). A versão que está nos respositórios do Debian/Ubuntu é ainda a antiga, 0.6, que partia do princípio que a autenticação no twitter se fazia através de username e password. Atualmente o Twitter usa uma forma mais rebuscada, pra evitar o envio desses por meios não seguros e possível roubo de identidade, utilizando um método de token. Você cria uma aplicação com um registro no Twitter e o mesmo pede autorização ao usuário pra se conectar em sua conta. Irei comentar melhor isso na parte 2.

Voltando à instalação no Debian/Ubuntu, que no momento em que escrevo estão nos releases 6.0.1 (Squeeze) e 10.10 (Maverick Meerkat), é preciso baixar o python-twitter, que está na versão 0.8.1, a partir do projeto em:

python-twitter-0.8.1.tar.gz

Essa versão de python já traz o novo método de autenticação baseado em oauth2. E falando nesse, o oauth que vem em Debian/Ubuntu também está desatualizado e não funiona com essa versão nova do Twitter que exige tokens. Então é necessário baixar a versão do site e instalar:

python-oauth2

Eu baixei a versão 1.5.167 e funcionou corretamente por aqui.

Eu já previamente deixei esses pacotes disponíveis aqui, mas com arquitetura i386. Mas deixei também os fontes pra gerar os pacotes.

python-oauth2_1.5.167_all.deb

python-oauth2_1.5.167.dsc

python-oauth2_1.5.167.tar.gz

python-twitter_0.8.2_all.deb

python-twitter_0.8.2.dsc

python-twitter_0.8.2.tar.gz

Se você tiver os pacotes build-essential e dpkg-dev, deve bastar pra iniciar a instalação. Basta descompactar o arquivo tar.gz utilizando o dsc, como por exemplo:

dpkg-source -x -b python-twitter_0.8.2.dsc

Isso criará o diretório do pacote, no caso python-twitter-0.8.2. Em seguinda basta entrar nesse diretório e gerar o pacote:

dpkg-buildpackage

Isso criará o pacote DEB na sua arquitetura.

Um olhar mais minuscioso notará que estou fornecendo o pacote 0.8.2 do python-twitter, que não existe no repositório ainda. Esse pacote contém uma atualização minha :-)

Com tudo isso instalado, já é possível testar se o python-twitter está instalado corretamente e na versão correta:


Python 2.6.6 (r266:84292, Sep 15 2010, 15:52:39) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import twitter
>>> twitter.__version__
'0.8.2'

Se ao invés disso você tiver a versão 0.8.1, a corrente do site, a maioria das coisas que fiz funcionarão, exceto a listagem de seguidores (followers).

Pink e cérebro

Details
Written by: Helio Loureiro
Category: Linux
Published: March 15, 2011
Hits: 10091

Faz alguns dias, comecei a ter o prazer de assistir os vídeos do YouTube em cor-de-rosa:

Principalmente no recente instalado Ubuntu 64 bits.  A princípio achei que era um dos tão mencionados problemas de flash em Linux 64, mas pude ver em alguns fóruns que isso tem afetado todos as versões de Linux pra Intel, tanto de 32 quanto 64 bits.

Consegui amenizar um pouco o problema com as dicas que encontrei aqui:

http://www.webupd8.org/2011/03/fix-pinkred-youtube-videos-bug-using.html

Mas o problema persiste.  Mais uma boa razão pra abandonar logo o flash, que é proprietário e depende unicamente da Adobe pra corrigir.

Embutindo MRTG em PHP

Details
Written by: Helio Loureiro
Category: PHP
Published: March 10, 2011
Hits: 6602

Com o lançamento do encurtador eri.cx, alguns novos desafios vão surgindo como o gerenciamento do mesmo.  Apesar das limitações, gosto de utilizar o MRTG para monitorar máquinas, ainda mais quando não posso demandar muito tempo administrando as mesmas.

Existem inúmeras ferramentas de monitração, como Nagios, mas nada é tão simples quanto o MRTG.  Mesmo tendo sido criado pra tráfego de rede via snmp, qualquer coisa pode ser monitorada por ele, sabendo que a aquisição de dados ocorre a cada 5 minutos somente.  

Se eu precisasse de algo com menor tempo ou maior granularidade, com certeza mudaria pra RRDtool.  Mas isso já daria muito mais trabalho e iria de encontro ao meu princípio de manter a coisa simples.

Pois eu tenho um script bem antigo, em Python, que gera a saída formatada para uso no MRTG pra monitorar qualquer coisa.  De discos à carga do sistema.  Basicamente a saída é:

INPUT
OUTPUT
UPTIME
HOSTNAME

Pra casos como o disco, pode-se usar tanto a quantidade de espaço livre quanto ocupado pra INPUT e OUTPUT.  Ou deixar INPUT e OUTPUT iguais, mostrando a quantidade de disco usado, por exemplo, em porcentagem.  Isso somente pra ilustrar os muitos usos do MRTG.  Como exemplo, logo abaixo, como configurar o MRTG para monitorar a carga do sistema, através da saída do comando "uptime".  Uma vez que MRTG não monitora números flutuantes, os valores são multiplicados por 100.

 


Target[load]: `/home/helio/bin/system.py load`
Options[load]: gauge,noinfo, nopercent, growright, unknaszero, nobanner
MaxBytes[load]: 2000
Title[load]: System load (100x)
PageTop[load]: <H1>System load (100x)</H1>
 <TABLE>
 <TR><TD>System:</TD><TD>eri.cx</TD></TR>
 <TR><TD>Maintainer:</TD><TD>Helio Loureiro</TD></TR>
 <TR><TD>Interface:</TD><TD>System load (10x)</TD></TR>
 <TR><TD>IP:</TD><TD>none</TD></TR>
 <TR><TD>Max wan traffic:</TD>
 <TD>2 Mbps</TD></TR>
 </TABLE>
YLegend[load]: System load (10x)
ShortLegend[load]: load (10x)

Na monitoração do eri.cx, acabei com uma imagem do site parecida com essa abaixo:

A finalidade desse artigo não é entrar muito a fundo na questão de configuração do MRTG ou de scripts de monitoração, mas discutir sobre a limitação de página criada pelo MRTG.  E como resolvi isso.  Como é possível notar, o MRTG não se integrou ao design da página corretamente pois não existem muitas opções pra adicionar CSS ou javascript no mesmo.

Na configuração do MRTG, adicionei apenas as entradas abaixo:


Extension[_]: php
AddHead[_]: <!--?php // Start YOURLS engine
 require_once( dirname(dirname(__FILE__)).'/includes/load-yourls.php' );
 require(dirname(dirname(__FILE__)).'/user/config.php' );
 yourls_html_head();
?--> <p><br> </p> <div align="center">Change already begun<br> <br> <br> <span style="font-weight: bold;" mce_style="font-weight: bold;"> PageFoot[_]: <br> </span></div> <p><br> </p> <p><br> </p> <--?php yourls_html_footer(); ?-->
Que são as mesmas que utilizo em todas as página para manter o mesmo estilo.  Só que o MRTG adiciona essas entradas no meio da página, dentro de um <BODY></BODY>.  Por isso o resultado ficou tão ruim.

Para solucionar isso, alterei a saída para:

Extension[_]: txt

removendo todo o restante.  Em seguida fiz uma alteração no código da página em PHP, para incluir o conteúdo do arquivo gerado:


<?php
$target = "load.txt";
$FD = fopen($target, "r");
$DATA = fread($FD, filesize($target));
fclose($FD);
#echo $DATA;
$status = 0;
$content = explode("\n", $DATA);
foreach ($content as $line) {
    if (ereg("<body>", $line)) {
        $status++;
    }
    if (ereg("</body>", $line)) {
        $status = 0;
    }
    if ($status) {
        echo $line."\n";
    }
}
?>

Isso fez com que a página gerada pelo MRTG ficasse dentro do PHP corretamente.  Como pode ser visto abaixo:

  • Goosfraba
  • Meu comentário na G1
  • Restart no Netgear com Python
  • Pidgin e Sametime
  • Adicionando o encurtador ao Choqok

Page 26 of 37

  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

Estatísticas

  • Users 2
  • Articles 482
  • Articles View Hits 3490157

Imagem aleatória