segunda-feira, 8 de outubro de 2018

41

Amanheceu um glorioso 7 de Setembro no planalto central. A esplanada dos ministérios estava pronta para o desfile da independência. Um enorme objeto de formas nunca vistas ocupava o gramado. Os primeiros trabalhadores a passarem por ali pensaram tratar-se de alguma estrutura para as comemorações cívicas. A polícia e o exército também não deram importância apesar do volume imenso que fazia sombra sobre vários prédios.

O desfile corria aborrecidamente igual a todos os outros quando uma abertura descomunal começou a surgir na lateral do objeto que ainda despertava curiosidade. O presidente estava ansioso para saber que novidade surgiria dali e apenas isso o mantinha acordado.

Assim que uma fresta formou-se, o objeto exalou uma fumaça densa. A abertura separou-se aos poucos e transformou-se lentamente numa rampa. Desceria algum cantor famoso? A multidão gritava e aplaudia.

Um ruído ensurdecedor calou a todos. O presidente ficou preocupado que essa falha técnica afetasse sua imagem.

Então, várias criaturas pequenas desceram pela rampa. Tinham a aparência de camarões, mas eram de vários tons de verde e alguns eram azulados. A multidão aplaudiu.

Duas dessas criaturas dirigiram-se ao palanque presidencial. A segurança ainda achava graça. Pararam a alguns metros das autoridades e exibiram uma figura com muitos riscos. Quarenta e um riscos.

Quando um soldado começou a ficar nervoso e apontou seu rifle aos visitantes, o som ensurdecedor tocou novamente. A dor intensa levou muitos ao chão.

Agora todos perceberam que aquilo não fazia parte do desfile. Eram visitantes de outro mundo! O pânico espalhou-se rapidamente e a multidão dispersou-se em minutos. Alguns poucos fãs de Guerra nas Estrelas ficaram para conversar com os visitantes.

- Quarenta e um é um número primo, disse um, orgulhoso de sua descoberta.

Buscaram um papel num ministério próximo e desenharam 43, o próximo número primo.

Os visitantes não pareceram muito satisfeitos. Trouxeram um pequena artefato de ferro e um desenho com 26 traços.

- Ferro! Vinte e seis é ferro! Eles querem o elemento quarenta e um. O que será isso?

A Wikipédia logo revelou sua utilidade: era nióbio. Os visitantes queriam nióbio.

O presidente reuniu-se com os militares e o ministro de minas e energia. Concordaram que haveriam de pedir algo em troca. Os visitantes adiantaram-se e fizeram uma pequena demonstração com uma arma que parecia emitir um laser.

Os militares ficaram muito impressionados e exigiram que o nióbio fosse trocado pelas armas novas. Fizeram um desenho com 1.000 riscos. Após extensa negociação mimética, o acordo final foi de cerca de mil toneladas de nióbio por 500 armas (os engenheiros da UnB estavam certos de que poderiam descobrir como produzir mais).

Os visitantes trabalharam durante meses levando a carga até uma nave que orbitava marte, cujo clima eles pareciam apreciar mais.

Finalmente, eles partiram e deixaram o pagamento com o exército.

Durante meses,  tentaram de todas as formas usar os artefatos sem qualquer sucesso e lamentando muito não terem pedido um manual, mesmo que em língua alienígena (não podia ser muito pior que chinês, afinal). Quando finalmente desistiram, entregaram um par de artefatos aos engenheiros do ITA e outro aos engenheiros da UnB.

Um mês depois, o ministro de Ciências e Tecnologia levou o resultado ao presidente.

- Presidente, o resultado não é bom.
- Fale logo, homem, qual o problema?
- É só uma luzinha que pisca.
- Putaquepariu! Como é que eu vou ficar? Entreguei uma fortuna para esses ETs por nada??
- Parece que sim, presidente.
- Engraçadinho, tu vais levar a culpa; eu é que não vou ficar mal com isso.
- Não se preocupe presidente, o povo não reclamou quando os gringos ficaram com o pré-sal. Isso não é nada.



quinta-feira, 4 de outubro de 2018

Extraindo a Versão de Vários Arquivos .class

Um erro "Bad version number in .class file" estava no meu caminho, então resolvi descobrir quais as diferentes versões de classes que estavam sendo usados.

A solução natural seria usar o executável javap, mas ele depende do classpath e aceita o nome de uma classe, não o nome de seu arquivo.

Felizmente, o Linux não nos deixa na mão em questão de manipulação de arquivos. O executável xxd permite extrair bytes quaisquer.

find /tmp -name *.class | xargs -I{} xxd -p -l4 -s4 {} | sort | uniq

Usei o find para encontrar todos os .class no diretório onde estavam. Depois o xargs usei para invocar o xxd com os seguintes parâmetros:
  • -p imprime o resultado de forma simplificada;
  • -l4 imprime quatro bytes;
  • -s4 inicia no byte 4 (quinto byte).
Finalmente, o sort | uniq elimina as repetições.
O resultado final foi:

00000031
00000032

E isso significa que há classes compiladas com java versão 5.0 e versão 6.0.

quinta-feira, 26 de abril de 2018

Escondendo Colunas no Oracle

Suponha que uma tabela RH.USUARIOS tenha uma coluna SENHA que, por qualquer motivo, não pode transformada num hash. É mais seguro delimitar quem pode ver os dados dessa coluna.

O primeiro passo é criar uma função guardiã para definir as situações em que os dados podem ser vistos. A função abaixo permite apenas que as consultas feitas a partir dos esquemas RH e XY possam ver o que há na coluna. Consultas feitas a partir de outros esquemas verão apenas null.

create or replace function 
hide_col( p_owner in varchar2, p_name in varchar2 ) 
return varchar2
as
begin
  if sys_context( 'userenv', 'session_user' ) in ('RH', 'XY')  then
    return null;
  else
    return '1=0';
  end if;
end;


O valor retornado pela função é usado como uma cláusula de um where, então null vai liberar o acesso. Esse mecanismo permite criar políticas mais complexas, mas neste caso apenas restringimos por esquema.

Depois, é preciso registrar a política de segurança com DBMS_RLS:

BEGIN
  DBMS_RLS.ADD_POLICY(object_schema=>'RH', object_name=>'USUARIO',
    policy_name=>'USUARIO_SENHA',
    function_schema=>'RH',
    policy_function=>'hide_col',
    sec_relevant_cols=>'SENHA',
    sec_relevant_cols_opt=>dbms_rls.ALL_ROWS);
END;


E agora as senhas estão protegidas.

sexta-feira, 6 de abril de 2018

Inflação Acumulada Com Funções Analíticas

Imagine ter uma tabela com os valores de inflação mês a mês:

create table ipca (
  data date,
  inflacao number(5,2)
);

Mas, além da inflação do mês, é preciso calcular a inflação acumulada dos últimos doze meses. Com funções analíticas, isso deve ser fácil. Entretanto, o SQL oferece sum() e count(), mas não uma função de agregação que multiplique os valores. Com um pouquinho de esperteza e logaritmos, isso pode ser superado. O segredo é somar os logaritmos e depois tirar o exponencial.

select data, inflacao, (exp(acumulada)-1)*100 from (
  select data, inflacao, 
    sum(ln(1+(inflacao/100)))
      over (order by dt_ipca desc rows between current row and 11 following)
        as acumulada
  from ipca
)

A janela de classificação é autoexplicativa: ela ordena os índices por data decrescente e olha o registro atual e os onze anteriores a ele.

Um índice 0,23% vira 1,0023 (1+0,23/100) e um índice -0,05% vira 0,9995 (1-0,05/100). Por isso, substrai-se 1 do exponencial. Se o índice for 0, o 1 o neutraliza. A soma dos logaritmos desses valores corresponde à multiplicação.

quarta-feira, 4 de abril de 2018

Limite de Conexões Simultâneas com IPTables

Eu precisava de uma maneira simples de limitar o número de conexões simultâneas a um servidor Apache. Há módulos para o Apache para isso, mas nenhum já instalado.

Então, resolvi experimentar algo de mais baixo nível: o iptables.

As regras para limitar a 4 conexões simultâneas o acesso às portas 80 e 443 são:

iptables -A INPUT -p tcp --syn --dport 80 \
  -m connlimit --connlimit-above 4 --connlimit-mask 32 \
  -j REJECT --reject-with tcp-reset
iptables -A INPUT -p tcp --syn --dport 443 \
  -m connlimit --connlimit-above 4 --connlimit-mask 32 \
  -j REJECT --reject-with tcp-reset
Mas eu precisava excluir uma faixa de IPs dessa limitação, então precedi aquelas regras com estas:

iptables -A INPUT -s 10.128.0.0/16 -p tcp --syn --dport 80 \
  -m connlimit --connlimit-above 4 --connlimit-mask 32 \
  -j REJECT --reject-with tcp-reset
iptables -A INPUT -s 10.128.0.0/16 -p tcp --syn --dport 443 \
  -m connlimit --connlimit-above 4 --connlimit-mask 32 \
  -j REJECT --reject-with tcp-reset
A lista das regras ficou assim:

# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source        destination
ACCEPT     tcp  --  10.128.0.0/16 anywhere    tcp dpt:http flags:FIN,SYN,RST,ACK/SYN #conn/32 > 4
ACCEPT     tcp  --  10.128.0.0/16 anywhere    tcp dpt:https flags:FIN,SYN,RST,ACK/SYN #conn/32 > 4
REJECT     tcp  --  anywhere      anywhere    tcp dpt:http flags:FIN,SYN,RST,ACK/SYN #conn/32 > 4 reject-with tcp-reset
REJECT     tcp  --  anywhere      anywhere    tcp dpt:https flags:FIN,SYN,RST,ACK/SYN #conn/32 > 4 reject-with tcp-reset

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
É importante gravar as configurações (este é o comando do CentOS):

# service iptables save
Saving firewall rules to /etc/sysconfig/iptables:          [  OK  ]
E posso verificar quantas conexões estão abertas assim:

netstat -anpt  | grep httpd  | awk '{print $5}' | \
  cut -d: -f4 | sort | uniq -c | sort -nr
Este comando filtra a lista de conexões abertas ao httpd, recorta o ip, conta o número de entradas de cada um e mostra o resultado ordenado por ordem decrescente.

quinta-feira, 1 de março de 2018

Bayes para Totós

Não achei nenhum texto satisfatório sobre Bayes, então resolvi fazer as contas para entender. Escrevi os resultados para a eventualidade de ser útil para alguém.

Comecei com o problema dos potes com biscoitos:
  1. O pote P1 tem 30 biscoitos de baunilha (b) e 10 de chocolate (c);
  2. O pote P2 tem 20 biscoitos de baunilha e 20 de chocolate.
Se eu tirar um biscoito de baunilha, qual a probabilidade de que seja do pote 1?

Bayes nos diz que P(P1|b)=[P(P1)P(b|P1)]/P(b).

Fiz todas as contas possíveis para entender o que isso quer dizer e cheguei a esses passos:
  1. P(P1) é a probabilidade de que um biscoito venha do pote 1 (50%, porque os potes têm 40 biscoitos cada um);
  2. P(b|P1) é a probabilidade de um biscoito de baunilha no pote P1 (75%, ou 30/40);
  3. Multiplicando P(P1)P(b|P1) temos a quantidade de biscoitos de baunilha vindos de P1. Temos 80 biscoitos e 30 deles são de baunilha e estão no pote P1. Isto é, 30/80 = 0,5*0,75.
  4. Dividindo esse valor por P(b), temos a probabilidade desses 30 biscoitos de baunilha em relação ao total de biscoitos de baunilha (50/80). Então, 30/80 divididos por 50/80 nos dá 0,6.
Então, a probabilidade de que um biscoito de baunilha venha do pote P1 é 60%.
O próximo exemplo é o do teste de câncer:
  1. 1% das pessoas têm câncer (99% não têm);
  2. 80% dos exames detectam a doença (20% falham);
  3. 10% dos exames são falsos positivos (as pessoas não têm a doença, mas o exame dá positivo).
Então, se o exame deu positivo para uma pessoa qualquer, qual a probabilidade de que ela realmente tenha a doença?

Bayes nos dirá que P(D|P)=[P(D)P(P|D)]/P(P), mas eu acho mais divertido pegar 1000 pessoas e metê-las em potes. Teremos o pote P (de positivo) e o pote N (de negativo). Nesses potes teremos as pessoas D (doentes) e as pessoas S (saudáveis, por enquanto).

Se aplicarmos o teste em todo mundo, vamos ter no pote P oito pessoas doentes (o teste identificou 80% dos casos) e 99 falsos positivos (dos 990 saudáveis, 10% foram falsamente classificados como doentes). O pote N terá os dois doentes exonerados pelo exame e os 891 que viverão um pouco mais.

Então, o pote P tem 107 pessoas (8 D e 99 S) e o pote N tem 893 (2 D e 891 S). Para alívio geral, a soma é 1000.

Então, voltando ao problema, se alguém tirar um exame positivo, qual a probabilidade de que esteja realmente doente? No fundo, o que buscamos é saber quantos estão realmente doentes no pote P. Será 8/(99+8), ou ~7,4%. Mas, para apaziguar Bayes, a conta seria:

P(D|P)=[P(D)P(P|D)]/P(P)=[0,01*0,8]/(107/1000)=8/107
Ou seja, um há grupo de 0,8% de pessoas que estão dentro de P e que estão doentes (0,01 * 0,8) e essas 8 pessoas representam 7,4% das pessoas cujo exame saiu positivo.

E depois vem aquele problema das três máquinas que produzem peças com defeitos. Trocando as máquinas por potes, fica bem mais fácil, como já deve ter ficado claro.

sexta-feira, 16 de fevereiro de 2018

Largura Máxima de Cada Coluna num CSV

Após tentar carregar um CSV cheio de inconsistências, resolvi buscar o tamanho máximo de cada coluna usando apenas a linha de comando no Linux.

O resultado é o comando que segue:

 head -1 arquivo.csv | \
 grep -Po ';' | \
 cat -n | \
 grep -Po '\d+' | \
 xargs -I'{}' bash -c "cut -d';' -f'{}' arquivo.csv | \
 awk 'length(\$0) > max { max=length(\$0) } END { print max }'"

Os passos são:
  1. Pegar a primeira linha (o cabeçalho);
  2. Elimina todos os caracteres exceto o separadores (para contar as colunas);
  3. Numera as colunas;
  4. Elimina os separadores para deixar apenas os números das colunas;
  5. Para cada coluna, executa um comando composto que retira a enésima coluna e imprime a largura do valor mais largo.
Então, para um cabeçalho do tipo COL1;COL2;COL3, os comandos de 1 a 4 produzem o seguinte:

 % head -1 arquivo.csv | grep -Po ';' |  cat -n | grep -Po '\d+'
 1
 2
 3


Depois, o xargs vai executar os seguintes comandos:

 bash -c cut -d';' -f'1' arquivo.csv | \
   awk 'length($0) > max { max=length($0) } END { print max }'
 bash -c cut -d';' -f'2' arquivo.csv | \
   awk 'length($0) > max { max=length($0) } END { print max }'
 bash -c cut -d';' -f'3' arquivo.csv | \
   awk 'length($0) > max { max=length($0) } END { print max }'

E o resultado final será uma lista de larguras:

 10
 25
 100

Para facilitar a leitura, dá para adicionar o número da coluna com um echo bem posicionado:

 head -1 arquivo.csv | \
 grep -Po ';' | \
 cat -n | \
 grep -Po '\d+' | \
 xargs -I'{}' bash -c "echo -n '{}: '; cut -d';' -f'{}' arquivo.csv | \
 awk 'length(\$0) > max { max=length(\$0) } END { print max }'"

E o resultado sairá assim:

 1: 10
 2: 25
 3: 100

Se faltar uma coluna, basta adicionar uma ao primeiro comando:

 bash -c "echo -n ';' &&  head -1 arquivo.csv"

Ou, sendo mais prático, basta usar o número de colunas e evitar a contagem:

 seq 1 3 | \
 xargs -I'{}' bash -c "cut -d';' -f'{}' arquivo.csv  | \
   awk 'length(\$0) > max { max=length(\$0) } END { print max }'"

O cabeçalho pode gerar problemas, quando suas colunas forem maiores que os dados propriamente ditos. A solução é usar o tail para pular a primeira linha.

 seq 1 3 | \
 xargs -I'{}' bash -c "tail -n +2 arquivo.csv | \
   cut -d';' -f'{}'  | \
   awk 'length(\$0) > max { max=length(\$0) } END { print max }'"

Isso vai falhar se o arquivo tiver campos com quebra de linha. Então, uma solução mais robusta pode ser obtida com um pouco de perl.

#!/usr/bin/perl
use Text::CSV_PP;
use List::Util qw(max);

my @max=();
my $csv=Text::CSV_PP->new({sep_char=>';',auto_diag=>1,binary=>1});
open(my $fh, '<:encoding(UTF-8)', $ARGV[0]) or die "Can't read file '$file' [$!]\n";
<$fh>; #Ignore header
while (my $line = $csv->getline($fh)) {
  my @fields=@$line;
  $max[$_]=max(length($fields[$_]),$max[$_]) for 0..$#fields;
};
print "@max\n";

Esse script recebe um único parâmetro: o nome de um arquivo. Ele percorre todas as linhas, exceto a primeira (ignorando o cabeçalho).