E foi-se a Python Brasil 9, em Brasília. Evento muito legal e que reuniu a comunidade de desenvolvedores python tupiniquins.
Eu participei falando um pouco do meu trabalho em telecomunicações. Minha apresentação foi essa abaixo, "python in telecommunications", que contou um pouco da história do nono dígito. Claro que não dá pra entender muito, uma vez que tem mais imagens que informação, mas mostra bem como python ajudou a ter uma solução mais robusta e limpa em relação a anterior, que rodava em shell script.
Dia desses eu redescobri as imagens da minha webcam. Tirei vários screenshots usando o aplicativo cheese, desde que minha mais nova nasceu. E nem lembrava disso.
Consegui criar uma videozinho com elas, o que foi bem legal, mostrando o crescimento dela (e minha barba ficando cada vez mais branca).
A idéia inicial era gerar um gif animado, mas o mesmo ficou em 85 MB de tamanho. E sem som.
Então resolvi fazer 2 coisas:
A captura do screenshot, eu consegui fazer utilizando pygame. O módulo já inclui vários binding pra realizar ações como capturar da webcam e salvar a imagem. O script ficou assim:
#! /usr/bin/python -u
"""
Not only Obamas _is_ watching you...
Based in: http://stackoverflow.com/questions/15870619/python-webcam-http-streaming-and-image-capture
"""
SAVEDIR = "/home/helio/Pictures/Webcam"
import pygame, sys
import pygame.camera
import time, random
pygame.init()
pygame.camera.init()
cam = pygame.camera.Camera("/dev/video0", (640,480))
while True:
print "Taking a shot:",
cam.start()
image = cam.get_image()
cam.stop()
timestamp = time.strftime("%Y-%m-%d_%H%M%S", time.localtime())
filename = "%s/%s.jpg" % (SAVEDIR, timestamp)
print "saving into %s" % filename
pygame.image.save(image, filename)
time.sleep(random.randrange(10) * 60)
Chamei de obamawatch.py em homenagem à espionagem da NSA nas nossas vidas, e que o presidente Obama não fez esforço nenhum pra diminuir ou mesmo evitar. É um script super intrusivo, pois tira fotos de tempos em tempos, podendo pegar situações que... humm... não o faça se sentir muito orgulhoso. Então é bom rodar de vez em quando.
Pra juntar as imagens JPEG geradas em um GIF animado, usei o imagemagick com o mogrify. Com o mogrify, na verdade, eu diminui as imagens pra 320x240 pixels, pra diminuir o tamanho. Então usei o imagemagick pra gera o GIF.
mogrify -resize 320x240 *jpg
gm convert -delay 20 2013-09-07_1* animated-2013-09-07.gif
Com isso consegui o resultado abaixo. Bem divertido.
Às vezes eu escrevo programas. Entre esse programas, alguns são daemons.
Além da confusão com demônios, o que são daemons?
Daemons são os programas que rodam em background no sistema, não precisando de um terminal (console) anexado. E qualquer tipo de programa pode ser um daemon, pra qualquer finalidade.
Em geral daemons seguem as seguintes regras pra se tornarem daemons:
O segundo fork() é feito para garantir que o programa, através do segundo processo filho, seja "herdado" pelo processo init do sistema.
Em Python, sempre incluo uma função como essa:
def Daemonize(self):
"""
Fork to became a daemon.
"""
if not self.isDaemon:
try:
self.run()
except KeyboardInterrupt:
sys.exit(0)
return
os.chdir("/")
pid = os.fork()
if (pid > 0):
sys.exit(os.EX_OK)
else:
pid = os.fork()
if (pid > 0):
sys.exit(os.EX_OK)
else:
self.run()
Esse é parte de um método, mas poderia ser uma função. A idéia é usar o getopt() para verificar as opções passadas e entrar no modo de daemon ou não, dependendo da opção passada, que modifica a variável booleana self.isDaemon.
Mas um dos meus programs começou a apresentar o seguinte erro:
helio@goosfraba:~$ connect_TSP.py ccn
IP already setup... skipping root access
Running as daemon
daemonized...
close failed in file object destructor:
IOError: [Errno 10] No child processes
Error in sys.excepthook:
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/apport_python_hook.py", line 66, in apport_excepthook
from apport.fileutils import likely_packaged, get_recent_crashes
RuntimeError: sys.meta_path must be a list of import hooks
Original exception was:
IOError: [Errno 10] No child processes
Inicialmente achei que era problema no "apport" com meu programa, que usa python-expect. Mesmo com tal erro, o programa funcionava perfeitamente em background, como daemon. Várias fontes na Internet, principalmente no Launchpad, o sistema de bug report do Ubuntu, várias pessoas reclamavam de tal erro como sendo problema do apport.
Após muito buscar a origem do problema, não no Ubuntu, mas no python, descobri que alguns file descriptors estavam causando esse erro, por continuarem abertos quando ocorria o fork(). Corrigi da seguinte forma:
def Daemonize(self):
"""
Fork to became a daemon.
"""
if not self.isDaemon:
try:
self.run()
except KeyboardInterrupt:
sys.exit(0)
return
os.chdir("/")
pid = os.fork()
if (pid > 0):
os.close(sys.stdin.fileno())
os.close(sys.stout.fileno())
os.close(sys.stderr.fileno())
sys.exit(os.EX_OK)
else:
pid = os.fork()
if (pid > 0):
os.close(sys.stdin.fileno())
os.close(sys.stdout.fileno())
os.close(sys.stderr.fileno())
sys.exit(os.EX_OK)
else:
self.run()
então bastou fechar os descritores de arquivo do STDIN, STDOU e STDERR pra ter certeza que o daemon não sairia com o erro acima.
Happy hacking :-)
Esse enviei 2 apresentações pra Python Brasil. Uma falando sobre python-twitter (e como faço pra enviar os #FF de sexta-feira) e outra pra falar sobre python em telecomunicações.
Não tenho nada escrito ainda, e vou aguardar a confirmação do trabalho pra começar. Se der certo, estarei em Brasília no início de outubro :-)
Para quem tem filho adolescente, sabe que as traquitanas tecnológicas tornaram a vida um inferno quando o assunto é estudar. Controlar o acesso ao computador é fácil, criando regras de bloqueio após certos horários estabelecidos, ou mesmo permissão e filtragem de acessos à sites baseados em URL e squid.
Mas com a chegadas dos dispositivos móveis, como telefones Android e tablets, a coisa não ficou tão fácil.
Restou a opção de bloquear o acesso pelo MAC address nos horários de estudo (que em geral são transformados em horários de soneca durante a tarde ou televisão).
Então fiz um pequeno script em python que bloqueia os MACs já cadastrados, tanto do tablet quanto do smartphone, e libera nos horários em que o computador desktop também está liberado. Com certeza isso não resolve o problema, mas com adolescentes, em geral, quase nada resolve.
O programa funciona com meu roteador wifi, um WR3G01, que comprei no Dealextreme (mais informações aqui). É um roteador wireless que se diz compatível com DD-WRT, mas que pra isso exige soldar os contatos da conexão serial na placa e carregar o firmware por tftp (era mais fácil dizer que não suporta então).
O programa segue:
#! /usr/bin/python
import re, urllib2
from base64 import encodestring
from sys import argv
MACS = [ "94:63:AA:BB:CC:DD", "74:E1:AA:BB:CC:DD" ]
login = "admin"
password = "admin"
server = "192.168.0.1"
port = 80
URI = "/apply.cgi"
TAG =""
for mac in MACS:
mac = re.sub(":",'%3A', mac)
TAG += mac + '%3B'
def Usage():
print "Use: %s {block|allow}" % argv[0]
exit(1)
if (len(argv) < 2):
Usage()
elif (not re.search("allow|block",argv[1])):
Usage()
msg_body = "submit_button=wlan_access&" + \
"change_action=&" + \
"action=Apply&" + \
"AccessControlList0=" + TAG + "&"
if (argv[1] == "allow"):
msg_body += "AccessPolicy0=0&"
elif (argv[1] == "block"):
msg_body += "AccessPolicy0=2&"
msg_body += "mac=&mac=&mac=&mac=&mac=&mac=&"
if (argv[1] == "allow"):
msg_body += "selectMAC=1"
elif (argv[1] == "block"):
msg_body += " selectMAC=3"
http_pass = encodestring(login + ":" + password)
url = "http://" + server + ":" + str(port) + URI
my_headers = {
'User-Agent' : 'Python Client/1.0/1.0',
'Authorization' : 'Basic ' + http_pass,
'Content-Type' : 'application/x-www-form-urlencoded'
}
req = urllib2.Request(
url,
headers = my_headers,
data = msg_body
)
print "Sending: %s" % argv[1]
resp = urllib2.urlopen(req)