Uma das grandes barreiras que encontramos ao iniciar o aprendizado de uma linguagem de programação é a falta de exercícios mais próximos da realidade. Talvez nem todo mundo veja essa dificuldade, mas pra mim sempre foi assim. Se não vejo uma forma de aplicar o que estou aprendendo, não consigo evoluir na linguangem.
Pegando como exemplo o livro da linguagem C escrito por Kerninghan e Ritchie: os exemplos são tão específicios que fica difícil abstrair as ideias e aprender como usar ponteiros de forma eficiente.
Para tentar superar essa barreira e ajudar a quem quiser aprender a programar sair da inércia e praticar surgiu o site "os programadores" ou http://osprogramadores.com. É um site criado e mantido pelos programadores Bernardino Campos, Marcelo Pinheiro, Marco Paganini e Sergio Correia pra ajudar iniciantes com desafios que são enviados através de "pull requests" no GitHub, ou seja, além de programar ainda existe a oportunidade de aprender controle de versão com git.
Não existe exigência de regularidade: é possível enviar seu código quando estiver disponível pra escrever. Também não existe uma linguagem só. Os desafios podem ser resolvidos em qualquer linguagem, de C à nodejs. E atualmente existe um sistema de scores pra ver quem está com maior pontuação de participação.
Eu só resolvi até agora os desafios 01 (que é criação e fork do repositório no github), 02 e 03 mas estou devendo de enviar meus pull requests. Fiz em python, go e C mas ainda quero escrever também em perl.
E tem mais uma parte interessante: grupo no telegram! Não bastando ter apenas o site, o grupo está lá pra ajudar com perguntas e respostas sobre os desafios e assuntos às vezes nem tão ligados à programação assim como por exemplo lubrificante pra ventoinha da cpu. O link é esse: https://t.me/osprogramadores
Então se tem interesse em aprender a programar ou aprender uma nova linguagem, não deixe pra depois e participe. É bem divertido e interessante.
Se existe algo que é um inferno em Linux é fonte. Como uso KDE como desktop padrão com HiDPI em 196 pontos, os aplicativos em GTK teimam em usar outras fontes e com tamanhos diferentes. É tipo uma maldição de Montezuma. Um dia seu ambiente gráfico está tudo certo, e de repente fica tudo com cara de que você tem problemas de visão.
E pior que os aplicativos escritos em GTK não respeitam o tamanho de fonte colocado no KDE. Uma das maneiras que encontrei foi usando comandos diretamente vi gsettings pra verificar o que os aplicativos em GTK têm configurados:
helio@xps13:~$ gsettings get org.gnome.desktop.interface text-scaling-factor helio@xps13:~$ gsettings get org.gnome.desktop.interface monospace-font-name helio@xps13:~$ gsettings get org.gnome.desktop.interface document-font-name helio@xps13:~$ gsettings get org.gnome.desktop.interface font-name helio@xps13:~$ gsettings get org.gnome.desktop.wm.preferences titlebar-font
e depois trocar pra padrões mais confortáveis.
helio@xps13:~$ gsettings set org.gnome.desktop.interface text-scaling-factor 0.5 helio@xps13:~$ gsettings set org.gnome.desktop.interface monospace-font-name 'Ubuntu Mono 10' helio@xps13:~$ gsettings set org.gnome.desktop.interface document-font-name 'Ubuntu 10' helio@xps13:~$ gsettings set org.gnome.desktop.interface font-name 'Ubuntu 10' helio@xps13:~$ gsettings set org.gnome.desktop.wm.preferences titlebar-font 'Ubuntu Bold 10'
E às vezes ainda preciso forçar um re-cache das fontes usadas com o comando:
helio@xps13:~$ sudo fc-cache -f -v
mas o resultado final é satisfatório (em geral basta fechar e abrir os programas pra encontrar a interface com as fontes corrigidas).
Quem tem filho sabe que não é fácil impor limites. Especialmente quanto uso de tablets ou smartphones ou até mesmo da TV. Eu tenho em casa um problema com YouTube que acaba virando a atração principal com um conteúdo bem longe de ser didático. Tentei até onde podia limitar no "YouTube é das 18 às 20" mas tenho fracassado miseravelmente.
Então resolvi apelar aos meus conhecimento computacionais e ao roteador por onde passa todo o tráfego da casa, e que roda open-wrt.
Criei um sistema básico em shell script que bloqueia tudo que é relacionado com YouTube em horários pré-determinados. Não ficou bonito, mas funciona.
#! /bin/sh
# save it into /usr/lib/scripts/firewall.sh
# and add into scheduled tasks as
# */5 * * * * /usr/lib/scripts/firewall.sh
hour=`date +%H`
minute=`date +%M`
echo "hour=$hour"
echo "minute=$minute"
status_file=/tmp/firewall_status
blocked_pattern="youtubei.googleapis.com googlevideo.com ytimg-edge-static.l.google.com i.ytimg.com youtube-ui.l.google.com www.youtube.com googleapis.l.google.com youtubei.googleapis.com video-stats.l.google.com ytimg-edge-static.l.google.com"
enable_firewall() {
echo "Enabling firewall"
for chain in INPUT FORWARD OUTPUT
do
count=1
for proto in tcp udp
do
for blocked in $blocked_pattern
do
echo iptables -I $chain $count -p $proto -m string --algo bm --string "$blocked" -j DROP
iptables -I $chain $count -p $proto -m string --algo bm --string "$blocked" -j DROP
count=`expr $count + 1`
done
done
echo iptables -I $chain $count -p udp --sport 443 -j DROP
iptables -I $chain $count -p udp --sport 443 -j DROP
count=`expr $count + 1`
echo iptables -I $chain $count -p udp --dport 443 -j DROP
iptables -I $chain $count -p udp --dport 443 -j DROP
count=`expr $count + 1`
done
}
disable_firewall() {
echo "Disabling firewall"
for chain in INPUT FORWARD OUTPUT
do
for proto in tcp udp
do
for blocked in $blocked_pattern
do
echo iptables -D $chain -p $proto -m string --algo bm --string "$blocked" -j DROP
iptables -D $chain -p $proto -m string --algo bm --string "$blocked" -j DROP
done
done
echo iptables -D $chain -p udp --sport 443 -j DROP
iptables -D $chain -p udp --sport 443 -j DROP
echo iptables -D $chain -p udp --dport 443 -j DROP
iptables -D $chain -p udp --dport 443 -j DROP
done
}
case $1 in
start) enable_firewall
echo -n "enabled" > $status_file
exit 0;;
stop) disable_firewall
echo -n "disabled" > $status_file
exit 0;;
esac
# possible status
# enabled|disabled
# start
# from 7:55-09:59
time_status=disabled
if [[ $hour -ge 7 && $hour -lt 10 ]]; then
time_status=enabled
if [[ $hour -eq 7 ]]; then
if [[ $minute -lt 55 ]]; then
time_status=disabled
else
time_status=enabled
fi
fi
fi
# from 12:00-17:59
if [[ $hour -ge 12 && $hour -lt 18 ]]; then
time_status=enabled
fi
# from 20:00-21:59
if [[ $hour -ge 20 && $hour -lt 22 ]]; then
time_status=enabled
fi
echo "time_status=$time_status"
current_status=
if [[ -f "$status_file" ]]; then
current_status=`cat $status_file`
fi
if [[ $current_status = $time_status ]]; then
echo "nothing to do"
else
if [[ $time_status = "enabled" ]];then
echo "loading firewall rules"
enable_firewall
fi
if [[ $time_status = "disabled" ]];then
echo "removing firewall rules"
disable_firewall
fi
echo -n $time_status > $status_file
fi
Basta salvar o arquivo em /usr/lib/scripts/firewall.sh e adicionar à crontab do open-wrt.
O código fonte está também publicado no github: https://github.com/helioloureiro/homemadescripts/blob/master/openwrt-firewall-block-youtube.sh
Toda história tem um começo, meio e fim. E esse momento chegou pro encurtador que criei em 14 de novembro de 2010. Inspirado no miud.in criado pelo Eduardo Maçan e baseado no yourls.org, criei o encurtador pra desafiar o "status quo" da empresa e mostrar que as coisas podiam funcionar de forma fácil e simples. A vida do encurtador foi legal com vários pontos altos mas ultimamente pude ver que eu era o único usuário costumaz do sistema. De resto eram só SPAMs e mais SPAMs. E custo.
O verdadeiro matador do encurtador foi custo. De algo em torno de 10 dólares em 2010, o custo bateu 70 dólares agora pra renovar. Alto demais pro pouco uso. Então decidi juntar o eri.cx ao panteão dos heróis e mitos da Internet e encerrar seu uso.
Foi divertido enquanto durou.
Mas tem um outro lado positivo. Registrei outro domínio dentro do .br com valor muuuuuito mais acessível: http://hl.eng.br
É basicamente o eri.cx só que com outro domínio. Então todas os links encurtados do passado funcionarão no novo. E com um adicional: CAPTCHA! Sim, menos SPAMs e mais links encurtados. E que dure 8 anos do eri.cx ou até mais. Longa vida ao http://hl.eng.br !
25 anos de alegrias e tristezas. Felizmente mais alegrias :)
Desses 25 anos, estamos juntos 20 anos. Acho. Talvez mais.
O herói de antigamente, quando eu comecei com Linux e BSD, era o sysadmin. Um lobo solitário que cuidava de um ou mais servidores com vários scripts de automação.
Essa figura desapareceu e cedeu lugar à padronização e máquinas virtuais. E as máquinas virtuais estão cada vez mais dando lugar aos containers.
Então a "arte" de fazer scripts está praticamente morrendo com poucos sysadmins ainda estudando perl, python, awk, etc. Mas mesmo na criação de containers é possível colocar scripts em prática e de forma proveitosa. Seriam o sysadmins 3.0.
Nesse sentido precisei criar um container pra rodar alguns programas em Java. Criei o container baseado no Ubuntu 16.04, que é a versão que tenho no laptop de trabalho e com Maven e Java. Maven é uma ferramenta de construção de Java parecida com pip no Python, CPAN no Perl, npm no node.js e rebar3 no Erlang. Ele baixa as dependências nas versões desejadas e compila tudo pra gerar seu arquivo jar. E também pode ser usado pra criar regras como de unittest.
Mas minha razão pra escrever esse post não é o Maven, que tem muitos outros artigos específicos muito bem escritos. É sobre o Java. Eu preciso rodar os meus programas com o JDK da Oracle. E como automatizar? Esse é o ponto dos scripts que comentei acima e razão desse artigo, de como usar ainda hoje em dia. Como o download do JDK da Oracle exige aceitação dos termos de uso, é preciso fazer isso dentro do script (através de cookies). O resto, é container puro.
O arquivo Dockerfile que criei é esse aqui:
FROM ubuntu:16latest ENV container docker ENV DEBIAN_FRONTEND noninteractive RUN apt-get install -y \ build-essential \ lsb-release \ tar \ wget \ unzip \ sudo \ git \ curl \ createrepo \ java-package \ fakeroot \ maven RUN apt-get install -y \ libgtk-3-0 \ libcairo-gobject2 RUN JAVA_URL=$(curl -sS http://www.oracle.com/technetwork/java/javase/downloads/index.html | \ grep -i href | \ grep "Download JDK" | \ head -1 | \ tr " " "\n" | \ grep href | \ sed "s|.*href=\"\([^\"].*[^\"]\)\".*|\1|" ); \ JDK_URL=$(curl -sS http://www.oracle.com$JAVA_URL | \ grep -i "^downloads.*linux.*tar.gz.*" | \ tr " ," "\n" | \ grep http | \ sed "s/.*\(http\)/\1/;s/\"//g" ); \ curl \ -SLkO \ --header "Cookie: oraclelicense=accept-securebackup-cookie" \ -# \ $JDK_URL ; \ linux_java=$(basename $JDK_URL) ; \ version=$(echo $linux_java | sed "s/.*-\([0-9][^_]*\)_.*/\1/" | cut -d "." -f 1) ; \ new_name=jdk-${version}-linux-x64.tar.gz ; \ deb_name=oracle-java${version}-jdk_${version}_amd64.deb; \ mv $linux_java /home/$new_name # uid=2000(jenkins) gid=2000(jenkins) groups=2000(jenkins),999(ssh-allowed),130(libvirtd),131(kvm),998(docker) RUN groupadd -g 2000 jenkins RUN useradd -g 2000 -u 2000 -m jenkins RUN su - jenkins -c "yes Y | fakeroot make-jpkg /home/jdk-*-linux-x64.tar.gz" RUN dpkg -i /home/jenkins/oracle-java*deb RUN JAVA_HOME=$(update-java-alternatives -l | grep oracle | tail -1 | awk '{print $NF}') ; \ JAVA_VERSION=$(update-java-alternatives -l | grep oracle | tail -1 | awk '{print $1}') ; \ cd $JAVA_HOME; \ ln -s jre .; \ update-java-alternatives -s $JAVA_VERSION; \ update-alternatives --install /usr/bin/java java $JAVA_HOME/bin/java 1; \ update-alternatives --set java $JAVA_HOME/bin/java
Eu tenho um outro container chamado ubuntu:16latest que é um Ubuntu 16.04 com apt dist-upgrade pra ter a versão mais recente. Eu geralmente adiciono o usuários jenkins pra poder rodar os containers dentro do jenkins.
Uma vez feita a configuração, basta construir o container. Como tenho vários arquivos pra fazer essas construções, eu geralmente salvo como Dockerfile.<distro>_<aplicativo>. Nesse caso específico salvei num arquivo Dockerfile.ubuntu_java. Para construir o container então:
docker build -t ubuntu:java -f Dockerfile.ubuntu_java .
Feito o container e salvo com a tag ubuntu:java, basta somente chamar o container pra rodar seus programas em java ou compilar.
helio@linux:~/DockerBuilds$ docker run -it ubuntu:java java -version java version "10.0.1" 2018-04-17 Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10) Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)
Daí pra frente é usar o Java como se estivesse instalado localmente.
Fontes:
1. https://wiki.debian.org/JavaPackage
2. https://www.debian.org/doc/manuals/debian-java-faq/ch5.html
Tive a oportunidade de participar de um curso profissional de containers oferecido dentro da empresa. Já tive oportunidade de participar de outro curso ministrado pela mesma escola sobre OpenStack numa outra vez. Material de primeira, laboratório completo e explicações concisas. Resolvi escrever um pouco pra quem busca algo nesse sentido pra saber se realmente vale a pena.
Meu conhecimento anterior de Dockers veio todo de documentos gerados por comunidade de usuários e pelo próprio site do Docker. Eu já uso Docker faz alguns anos e com bastante desenvoltura. Muita coisa que preciso fazer já é em containers. Durante o curso percebi que realmente não tinha muita novidade. O uso básico de docker com "run", "images", "tag", etc é exatamente o mesmo.
Uma diferença notável que eu não conhecia foi a referência quanto à OCI, Open Container Iniciative, que tem agora um binário "runc" que substituirá o comando "docker" pra criar uma abstração genérica no manuseio de containers. Os argumentos são exatamente os mesmo que do "docker". Então um "docker run -it ubuntu:latest bash" funciona com um "runc run -it ubuntu:latest bash". Parte disso é pra facilitar o uso com kubernetes.
Agora sobre kubernetes, eu não conhecia tão a fundo. Sempre usei um básico do básico do básico com minikube na maioria das vezes. Já tinha mexido com Jagger pra alguma monitoração de tempo em containers, mas nada como vi no curso. O laboratório funcionando perfeitamente e com vários exercícios. Mas várias vezes consultamos a documentação online do kubernetes, ou seja, a documentação já disponível é riquíssima. Mesmo em alguns pontos o material didático estava bem atrás dos comandos disponíveis no último release de kubernetes.
E o material do curso? Sinto mas é um material copyrighted que não pode ser distribuído. E deixaram isso bem claro. Mas eu pessoalmente acho que a documentação com tutoriais disponíveis cobrem muito bem tudo que estava descrito ali.
Vale então fazer um curso pago de containers? Eu diria que depende. Se tem tempo, é possível aprender tudo sozinho em seu próprio computador. Mas se tiver uma necessidade mais imediata de resultados, sim cursos serão um bom acelerador nessa direção.
Qualquer que seja sua decisão, keep calm and runc -it run.
Escrevi um artigo pro blog "ChurrOps on DevOps" sobre raspresenterpy aṕos uma conversa sobre o assunto no canal do Telegram do grupo "papo de sysadmin".
Eu achava que já tinha escrito sobre isso aqui mesmo, mas fiquei surpreso por não ter encontrado nenhuma referência.
raspresenterpy é um programa de display que alterna links pra ser usado pra mostrar mensagens ou telas de monitoramento como do Jenkins. Fiz até uma apresentação sobre o mesmo numa PyConSE. O texto que escrevi detalha sobre repositório e como usar o mesmo.
Ele encontra-se com suporte ao raspbian Jessie e python-qt4. Testei com python-qt5 e já verifiquei que não funciona. Então deixei no meu backlog pra corrigir e lançar uma versão mais recente.
https://churrops.io/2018/06/08/usando-python-e-raspberrypi-pra-mostrar-varias-telas/
Feliz dia da toalha (ou dia do orgulho nerd). Pegue sua toalha e... não entre em pânico.
Já teve problemas com a barra de rolagens (ou scrolls)? Não? Pois eu tive por muito tempo.
Como uso KDE, aplicativos baseados em GTK teimam em ignorar os padrões que seleciono e fazem as coisas da forma que querem. Talvez por isso eu não use Gnome.
Mas esse problema vem me atormentando desde que foi lançado o GTK3. Faz o quê? Uns 5 anos? As teclas de rolagem estavam com um padrão de que apareciam somente quando o mouse passava por cima. Irritante. Não sei quem teve essa ideia, mas eu nunca gostei. Só que não conseguia mudar. E eu não dei muita importância ao fato já que no fim conseguia fazer o que precisava e isso era algo pequeno.
E hoje finalmente eu cheguei no ponto de me incomodar e procurar um solução, pois no inkscape é onde esse treco incomoda mais. Os botões de rolagem ficam se sobrepondo entre o da imagem e da paleta de cores. Às vezes queria mover, fazia no outro.
E até que não foi algo tão complicado quanto eu esperava. Como já imaginava, era algo configurável via interface gráfica no caso de estar usando Gnome ou Unity. Mas pra quem não os têm, via linha de comando basta usar o seguinte:
gsettings set com.canonical.desktop.interface scrollbar-mode normal
Não gostou e quer voltar ao padrão antigo (por mais que eu não entenda esse tipo de decisão)?
gsettings set com.canonical.desktop.interface scrollbar-mode overlay-auto
É bem simples. Sofri à toa por besteira.
Esses dias estava analisando um código que era parecido com shell script. Sua função era monitorar as interfaces de rede para, no caso de alteração (nova interface surgindo ou desaparecendo), registrar a mudança de objetos no sistema de dados do opensaf (objetos no IMM pra quem conhece).
Mas o código fazia algo parecido com um shell que a todo healthcheck do AMF (framework do opensaf que é muito semelhante ao systemd e roda determinado programa ou script de tempos em tempos) fazia uma busca com o comando "ip link addr list" e comparava com o que estava armazenado no IMM. Algo como:
def healthcheckCallback(self, invocation, compName, healthcheckKey): saAmfResponse(self.check_macs, invocation, eSaAisErrorT.SA_AIS_OK)
def check_macs(self):
macs = []
for line in self.get_link_list().split("\n"):
if not re.search("link/ether", line): continue
# [ 'link/ether', '52:54:00:c3:5d:88', 'brd', 'ff:ff:ff:ff:ff:ff']
mac.append(line.split()[1])
imm_obj = self.get_from_imm()
if imm_obj != macs:
self.update_imm(mac)
def get_link_list(self):
linux_command = "ip link list"
return self.run_shell(linux_command)
Essa é uma forma bastante simplificada pra tentar visualizar como tudo funciona. Eu propositalmente tirei comentários extras e deixei mais limpo apenas para poder comentar aqui.
Como a chamada pra buscar os dados junto ao IMM no OpenSAF tem muitas linhas, eu só deixei um get_from_imm() que retornará um array de mac registrados anteriormente. Se esse valor for diferente do coletado, então é chamado o método update_imm() com os macs que devem estar lá.
Funciona? Sim, funciona. Mas... se não houve nenhuma mudança nas interfaces de rede (como a subida de uma interface de VIP ou mesmo um container em docker), por quê eu preciso rodar o get_link_list()?
Entendeu qual foi meu ponto?
O código em si consiste em rodar o monitoramente separado numa thread. Toda vez que o código detecta uma mudança (na verdade o kernel sinaliza isso), ele altera uma variável que o programa lê durante o healthcheck. Algo como:
def check_macs(self):
if self.network_changed is False: return
Assim bem simples. Teve mudança? network_changed vira um True.
Linux tem mecanismos pra detectar uma mudanças na interfaces de rede. Por quê não usar? E foi o que fiz.
Criei um método chamado monitor_link() que é iniciado junto com programa no método initialize(), que é parte de como o AMF faz as chamadas de callback:
self.thread = threading.Thread(target=self.monitor_link, args=())
self.thread.start()
E como funciona o monitor_link()? Aqui tenho de pedir desculpas antecipadamente que enquanto o código utiliza menos CPU e memória que chamar um shell script, o tamanho e complexidade é bastante grande. No fim troquei 2 linhas de código por umas 35 linhas. Na verdade eu praticamente escrevi o código por trás do "ip link". Mas o resultado ficou independente desse comando e mesmo de utilizar um shell externo pra buscar o resultado.
A primeira coisa é criar um socket do tipo AF_NETLINK. Em seguida fazer um bind() num ID aleatório e monitorar com RTMGRP_LINK.
def monitor_link(self): # Create the netlink socket and bind to RTMGRP_LINK, s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE) s.bind((os.getpid(), RTMGRP_LINK))
pra gerar o código aleatório que é um inteiro, usei os.getpid() pra usar o PID do próprio programa.
Em seguida é iniciado um loop com select() em cima do descritor do socket pra leitura. Quando aparecer algum dado, daí sim a informação é lida.
rlist, wlist, xlist = select.select([s.fileno()], [], [], 1)
O que vem a seguir são quebras da sequência de bits até chegar no ponto é possível ver o tipo de mensagem que chegou do select(). Se o tipo de mensagem for NOOP de algo nulo, apenas continue monitorando no select(). Se vier algum ERROR, pare o programa. Se vier mensagem e não for do tipo NEWLINK pra um link novo ou mudança de MAC, também continue aguardando no select().
if msg_type == NLMSG_NOOP: continue elif msg_type == NLMSG_ERROR: break elif msg_type != RTM_NEWLINK: continue
Por fim uma iteração nos dados pra buscar o tipo. Se o dado for do tipo IFLA_IFNAME, que é uma nova interface ou mudança de nome, ou IFLA_ADDRESS, que é MAC e endereço IP, muda a flag de network_changed pra True.
rta_type == IFLA_IFNAME or rta_type == IFLA_ADDRESS:
E é isso. O código completo segue abaixo.
def monitor_link(self): # Create the netlink socket and bind to RTMGRP_LINK, s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE) s.bind((os.getpid(), RTMGRP_LINK)) while self.terminating is False: rlist, wlist, xlist = select.select([s.fileno()], [], [], 1)
if self.network_changed is True: continue if self.terminating is True: return
try: data = os.read(rlist[0], 65535) except: continue msg_len, msg_type, flags, seq, pid = struct.unpack("=LHHLL", data[:16]) if msg_type == NLMSG_NOOP: continue elif msg_type == NLMSG_ERROR: break elif msg_type != RTM_NEWLINK: continue data = data[16:] family, _, if_type, index, flags, change = struct.unpack("=BBHiII", data[:16]) remaining = msg_len - 32 data = data[16:] while remaining: rta_len, rta_type = struct.unpack("=HH", data[:4]) if rta_len < 4: break rta_data = data[4:rta_len] increment = (rta_len + 4 - 1) & ~(4 - 1) data = data[increment:] remaining -= increment if rta_type == IFLA_IFNAME or rta_type == IFLA_ADDRESS: self.network_changed = True
Encontrei essa implementação no Stack Overflow buscando informação do código em C. Foi uma grande ajuda e deixou meu programa muito mais coerente com o que eu realmente queria.
Ficou muito maior? Ficou. Mas também ficou muito mais 1337 :)
Mais:
[1] Exemplo de uso de python com AMF no OpenSAF: https://sourceforge.net/p/opensaf/staging/ci/default/tree/python/samples/amf_demo
[2] Projeto OpenSAF: https://sourceforge.net/projects/opensaf/
[3] Implementação original desse código: https://stackoverflow.com/questions/44299342/netlink-interface-listener-in-python
Tenho escrito pouco aqui, e já passei do prazo de fazer uma retrospectiva de 2017. Mas antes tarde do que nunca.
Ao invés de escrever, resolvi aderir ao momento multimídia do mundo e fazer em vídeo. O primeiro é um vídeo de fotos e vídeos meus que peguei direto no Google, no storage onde estão conectados meu smartphone e meu tablet.
O segundo é da coleção de snapshots que o raspberrypi tira a cada 15 minutos da janela de casa. Dá pra ver que no começo e no fim fica bem escuro, enquanto que no meio é mais claro (dias mais curtos no inverno e mais longos no verão).
Eu gosto de usar os kernels mais recentes e compilar minhas próprias versões. Às vezes adiciono patches pra melhorar resposta de temporeal, às vezes um suporte pra AUFS no docker.
Atualmente estou com o 4.14-rc2, um pouco antigo nesse momento mas que me deu um uptime de 25 dias sem problemas até agora.
Linux elxaf7qtt32 4.14.0-rc2-helio #12 SMP PREEMPT Fri Sep 29 13:50:19 CEST 2017 x86_64 x86_64 x86_64 OSI/Linux
(é... troquei GNU/Linux por OSI/Linux... por diversão e usando sed na saída do uname)
E descobri que o VirtualBox versão 5.2 simplesmente não consegue rodar o comando /sbin/vboxconfig. Olhando o erro um pouco mais de perto pelo log:
gcc -Wp,-MD,/tmp/vbox.0/linux/.SUPR0IdcClient-linux.o.d -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/5/include -I./arch/x86/include -I./arch/x86/include/generated -I./include - I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -D__KERNEL__ -Wall -Wundef -Wstrict-prototyp es -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -Werror-implicit-function-declaration -Wno-format-security -std=gnu89 -fno-PIE -mno-sse -mno-mmx -mno-sse2 -mno-3dnow - mno-avx -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -funit-at-a-time - DCONFIG_X86_X32_ABI -DCONFIG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -DCONFIG_AS_CFI_SECTIONS=1 -DCONFIG_AS_FXSAVEQ=1 -DCONFIG_AS_SSSE3=1 -DCONFIG_AS_CRC32=1 -DCONFIG_AS_AVX=1 -DCONFIG _AS_AVX2=1 -DCONFIG_AS_AVX512=1 -DCONFIG_AS_SHA1_NI=1 -DCONFIG_AS_SHA256_NI=1 -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -fno-delete-null-pointer-checks -O2 --param=allow- store-data-races=0 -DCC_HAVE_ASM_GOTO -Wframe-larger-than=1024 -fstack-protector-strong -Wno-unused-but-set-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-var-tracki ng-assignments -g -gdwarf-4 -pg -mfentry -DCC_USING_FENTRY -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -fconserve-stack -Werror=implicit-int -Werror=strict-pro totypes -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -include /tmp/vbox.0/include/VBox/SUPDrvMangling.h -fno-pie -I/lib/modules/4.14.0-rc2-helio/build/i nclude -I/tmp/vbox.0/ -I/tmp/vbox.0/include -I/tmp/vbox.0/r0drv/linux -D__KERNEL__ -DMODULE -DRT_OS_LINUX -DIN_RING0 -DIN_RT_R0 -DIN_SUP_R0 -DVBOX -DRT_WITH_VBOX -DVBOX_WITH_HARDENING -Wno-declaration-after-statement -DRT_ARCH_AMD64 -DVBOX_WITH_64_BITS_GUESTS -DMODULE -DKBUILD_BASENAME='"SUPR0IdcClient_linux"' -DKBUILD_MODNAME='"vboxnetflt"' -c -o /tmp/vbox.0/lin ux/.tmp_SUPR0IdcClient-linux.o /tmp/vbox.0/linux/SUPR0IdcClient-linux.c if [ "-pg" = "-pg" ]; then if [ /tmp/vbox.0/SUPR0IdcClient.o != "scripts/mod/empty.o" ]; then ./scripts/recordmcount "/tmp/vbox.0/SUPR0IdcClient.o"; fi; fi; (cat /dev/null; echo kernel//tmp/vbox.0/vboxnetflt.ko;) > /tmp/vbox.0/modules.order if [ "-pg" = "-pg" ]; then if [ /tmp/vbox.0/linux/SUPR0IdcClient-linux.o != "scripts/mod/empty.o" ]; then ./scripts/recordmcount "/tmp/vbox.0/linux/SUPR0IdcClient-linux.o"; fi; fi; if [ "-pg" = "-pg" ]; then if [ /tmp/vbox.0/VBoxNetFlt.o != "scripts/mod/empty.o" ]; then ./scripts/recordmcount "/tmp/vbox.0/VBoxNetFlt.o"; fi; fi; /tmp/vbox.0/linux/VBoxNetFlt-linux.c: In function ‘vboxNetFltLinuxSkBufFromSG’: /tmp/vbox.0/linux/VBoxNetFlt-linux.c:741:24: error: ‘SKB_GSO_UDP’ undeclared (first use in this function) fGsoType = SKB_GSO_UDP; ^ /tmp/vbox.0/linux/VBoxNetFlt-linux.c:741:24: note: each undeclared identifier is reported only once for each function it appears in In file included from /tmp/vbox.0/include/iprt/types.h:29:0, from /tmp/vbox.0/r0drv/linux/the-linux-kernel.h:34, from /tmp/vbox.0/linux/VBoxNetFlt-linux.c:24: /tmp/vbox.0/linux/VBoxNetFlt-linux.c: In function ‘vboxNetFltLinuxCanForwardAsGso’: /tmp/vbox.0/linux/VBoxNetFlt-linux.c:1276:53: error: ‘SKB_GSO_UDP’ undeclared (first use in this function) if (RT_UNLIKELY( skb_shinfo(pSkb)->gso_type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCPV6 | SKB_GSO_TCPV4) )) ^ /tmp/vbox.0/include/iprt/cdefs.h:1616:53: note: in definition of macro ‘RT_UNLIKELY’ # define RT_UNLIKELY(expr) __builtin_expect(!!(expr), 0) ^ /tmp/vbox.0/linux/VBoxNetFlt-linux.c: In function ‘vboxNetFltLinuxForwardToIntNetInner’: /tmp/vbox.0/linux/VBoxNetFlt-linux.c:1526:47: error: ‘SKB_GSO_UDP’ undeclared (first use in this function) if ( (skb_shinfo(pBuf)->gso_type & (SKB_GSO_UDP | SKB_GSO_TCPV6 | SKB_GSO_TCPV4)) ^ scripts/Makefile.build:311: recipe for target '/tmp/vbox.0/linux/VBoxNetFlt-linux.o' failed make[2]: *** [/tmp/vbox.0/linux/VBoxNetFlt-linux.o] Error 1 Makefile:1498: recipe for target '_module_/tmp/vbox.0' failed make[1]: *** [_module_/tmp/vbox.0] Error 2 /tmp/vbox.0/Makefile.include.footer:97: recipe for target 'vboxnetflt' failed
Foi algo relacionado com a variável SKB_GSO_UDP, que parece não existir no kernel mais recente ou no VirtualBox.
Buscando pelo termo pude encontrar que realmente existe uma diferença entre ambos um patch precisa ser aplicado, descrito aqui:
Fonte: https://gist.github.com/herbmillerjr/039c129e9c25b047b906e19ad1f23a59
diff --git a/work/vboxnetflt/linux/VBoxNetFlt-linux.c b/work/vboxnetflt/linux/VBoxNetFlt-linux.c index f824654..b61d82c 100644 --- work/vboxnetflt/linux/VBoxNetFlt-linux.c +++ work/vboxnetflt/linux/VBoxNetFlt-linux.c @@ -126,6 +126,10 @@ typedef struct VBOXNETFLTNOTIFIER *PVBOXNETFLTNOTIFIER; # endif #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +#define SKB_GSO_UDP 0 +#endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 20, 0) # define VBOX_HAVE_SKB_VLAN #else diff --git a/work/vboxpci/linux/VBoxPci-linux.c b/work/vboxpci/linux/VBoxPci-linux.c index 2dbf47f..e361ef3 100644 --- work/vboxpci/linux/VBoxPci-linux.c +++ work/vboxpci/linux/VBoxPci-linux.c @@ -353,12 +353,16 @@ static void vboxPciFileClose(struct file* file) static int vboxPciFileWrite(struct file* file, unsigned long long offset, unsigned char* data, unsigned int size) { int ret; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(file, data, size, &offset); +#else mm_segment_t fs_save; fs_save = get_fs(); set_fs(get_ds()); ret = vfs_write(file, data, size, &offset); set_fs(fs_save); +#endif if (ret < 0) printk(KERN_DEBUG "vboxPciFileWrite: error %d\n", ret);
Mas como minha versão de VirtualBox não era exatamente a mesma que essa descrita, acabei fazendo o patch manualmente, que adiciona a variável SKB_GSO_UDP ao VirtualBox caso seja um kernel 4.14.0 ou superior. Os arquivos para serem modificados no Ubuntu (e possivelmente Debian) são:
depois basta rodar novamente como root o comando /sbin/vboxconfig para criar os módulos de kernel necessários pro funcionamento do VirtualBox.
SKB: SocKet Buffer
GSO: Generic Segmentation Offloading
UDP: User Datagram Protocol
Não tenho idéia do que isso faz exatamente mas parece ser uma chamada de um pacote UDP encapsulado.
Page 11 of 33