2024-11-09

Sobre vacas y pampas

¡Qué fea que es la vegetación nativa de las pampas! Toda dura, con pinches, casi sin árboles. ¿Por qué es así? ¿Siempre fue así?

¿Será culpa de los españoles? Ellos soltaron vacas en las pampas. Las vacas se reprodujeron un montón porque no hay animales que las puedan comer en la pampas. ¿Sabemos cómo eran las pampas antes de las vacas? ¿Hay alguna historia transmitida oralmente por los pueblos originarios? ¿Hay algún estudio serio? ¿Dónde está el paper con referato que lo estudie?

Yo creo que lo que conocemos de la vegetación nativa de las pampas es lo que sobrevivió a las vacas, que se comieron todo a su paso mientras explotaron poblacionalmente en las pampas por varios siglos, hasta que la campaña de Roca terminó de conquistar la zona. Por eso es todo pinchudo y duro.

Restauremos las pampas. Plantemos árboles, plantemos pastos blanditos. La vegetación no tiene que lastimarnos.



2024-09-05

heic a jpg

Estoy en ubuntu, me conecto al celu y las fotos están en formato .heic ¯_(ツ)_/¯. Pero quiero un .jpg! Ah! Ya sé, googleo y encuentro esto. Para convertir un montón de archivos .heic a .jpg hay que instalar libheif-examples en la consola así:

$ sudo apt-get install libheif-examples

Y después se puede correr esto:

$ for file in *.heic; do heif-convert $file ${file/%.heic/.jpg}; done

Para convertir todos los .heic de un directorio.

Ajustalo si querés hacer otra cosa.

2023-08-08

Lubuntu sin GUI

Ayer a la noche arranco mi notebook, que tiene instalado lubuntu 22.04, y en vez de abrir la GUI de siempre me abre la terminal en texto. Por suerte se sigue conectando al wifi. Así que podía instalar cosas en línea de comando. Cuando rebooteo veo que por un momento aparece el error ACPI BIOS Error (bug): Could not resolve symbol [\CTDP]

Después de tratar de tocar grub para que bootee distinto, infructuosamente, encontré una guía para instalar guis en ubuntu. En ella dice que la forma más fácil para instalar una gui es usar la utilidad tasksel, y que puedo instalar la gui de lubuntu con eso, instalando la gui lxqt. Así que hice sudo apt install tasksel, sudo tasksel, elegí lxqt, esperé a que termine, rebootié, y ahora tengo gui de nuevo!

2023-01-27

Aceptar invites a repos de github desde línea de comando

Si por algún motivo querés aceptar invites a repos desde línea de comando en github, se puede!

Para hacer eso, hay que usar el comando gh y tener estar autenticado via 

$ gh auth login

Una vez que estás autenticado, hay que usar el api de github directamente via gh api, que maneja la autenticación por nosotros y recibe parámetros de forma análoga a curl.

El comando para obtener todos los invites pendientes es:

$ gh api user/repository_invitations

y devuelve un array con todas las invitaciones pendientes del usuario (en json).

Lo importante de ese array es que cada entrada tiene un campo "id". Es algo así:

[
  {
    "id": 12345678,
    "repository": {
      "full_name": "usuario/repo",
      "otras": "cosas",
    },
    "otras": "cosas"
  },
  { "id": "23456789", "otras": "cosas" }
]

Con ese id, podés correr este otro comando para aceptar el invite así:

$ gh api -X PATCH user/repository_invitations/12345678

y si salió todo bien ahora podés hacer clone de ese repo privado así:

$ gh repo clone usuario/repo 

Espero que les sirva!

PD: Las llamadas a la API las deduje de acá.

2022-09-21

estimate-sol

Hoy publiqué una herramienta open-source que sirve para medir el tamaño de contratos de solidity. Está en pypi, y podés instalarla corriendo pip install estimate-sol. Este es el uso:

$ estimate-sol -h
usage: estimate-sol [-h] [--punctuations] [--encoding ENCODING] [--dependencies-multiplier DEPENDENCIES_MULTIPLIER]
[--assembly-multiplier ASSEMBLY_MULTIPLIER] [--week-size WEEK_SIZE] [-a] [-V]
files [files ...]

Estimate solidity audit sizes.

positional arguments:
files Files to be audited.

optional arguments:
-h, --help show this help message and exit
--punctuations, -p Use punctuations for counting (default: lines)
--encoding ENCODING, -e ENCODING
File encoding (default=utf-8).
--dependencies-multiplier DEPENDENCIES_MULTIPLIER, -d DEPENDENCIES_MULTIPLIER
Multiplier to estimate non-audited dependencies (default=1.0).
--assembly-multiplier ASSEMBLY_MULTIPLIER
Multiplier to estimate assembly code (default=1.0).
--week-size WEEK_SIZE, -w WEEK_SIZE
Number of items to audit per week. Defaults: (lines:1.0, punct:1.0)
-a, --absolute_paths Show all paths as absolute (by default relative paths are shown if inside current directory).
-V, --version show program's version number and exit
Y podés ver el anuncio original acá.

2022-09-09

chromium en snap via ssh

Me conecto a una VM por ssh, forwardeando X (ssh -X).

Instalo el snap de chromium (sudo snap install chromium).

Lo corro, y me da este error.

$ chromium
X11 connection rejected because of wrong authentication.
[2050513:2050513:0909/155104.874445:ERROR:ozone_platform_x11.cc(240)] Missing X server or $DISPLAY
[2050513:2050513:0909/155104.874518:ERROR:env.cc(255)] The platform failed to initialize.  Exiting.
[1]+  Done                    XAUTHORITY=$HOME/.Xauthority chromium

Para arreglar el problema hay que setearle la variable de entorno de XAUTHORITY (¿por qué?).

Lo corro así y problema solucionado.

$ XAUTHORITY=$HOME/.Xauthority chromium

2022-08-28

ssh-agent no anda en lubuntu 22.4

Este fin de semana upgradié el sistema operativo de mi notebook. Lo pasé de lubuntu 20.4 a lubuntu 22.4.1. Cuando terminé todo anduvo razonablemente bien (booteó!) pero el agente de ssh dejó de arrancar solo.
Estuve un rato googleando y no encontraba nada. Así que me puse a buscar como arranca el ssh-agent. Para eso tiré un find con un grep en mi home.
 
$ find . -type f -print0 | xargs -0 -n 1000 fgrep -n ssh-agent
 
Hice eso y encontré que en el archivo $HOME/.xsession-errors tenía este mensaje de error:
 
/etc/X11/Xsession.d/90x11-common_ssh-agent: line 9: has_option: command not found
 
¡Bingo!
Googlié el error y encontré este reporte de bug de ubuntu. Dentro de los comentarios encontré la solución.
La misma consiste en poner en el directorio /etc/X11/Xsession.d/ un archivo más para definir has_option. El archivo yo lo nombré 01x11-has_option-aure y tiene este contenido:
 
# This file is sourced by Xsession(5), not executed.
#
# workaround created by Settel
# defines "has_option" unless it is already defined
# see https://bugs.launchpad.net/ubuntu/+source/xorg/+bug/1922414
#


if type -t has_option >/dev/null; then
  return
fi


OPTIONS="$(
  if [ -r "$OPTIONFILE" ]; then
    cat "$OPTIONFILE"
  fi
  if [ -d /etc/X11/Xsession.options.d ]; then
    run-parts --list --regex '\.conf$' /etc/X11/Xsession.options.d | xargs -d '\n' cat
  fi
)"

has_option() {
  # Ensure that a later no-foo overrides an earlier foo
  if [ "$(echo "$OPTIONS" | grep -Eo "^(no-)?$1\>" | tail -n 1)" = "$1" ]; then
    return 0
  else
    return 1
  fi
}


2022-08-27

Cómo hago sobrecarga progresiva

 Hace 5 meses empecé a hacer pesas en el gimnasio que está a una cuadra de casa. Y estoy ganando fuerza. Para ganar fuerza en el gimnasio hay que hacer una cosa que se llama "sobrecarga progresiva". Eso quiere decir que a medida que vamos pudiendo, hay que ir haciendo los ejercicios cada vez más difíciles. En el caso de hacer pesas eso lo podés hacer subiendo el peso que usás en el ejercicio, subiendo la cantidad de repeticiones por serie y/o subiendo la cantidad de series. El problema es que subir el peso todo el tiempo no se puede porque las mancuernas suben de a 2.5kg, y los discos también (por lo que como hay que poner cantidades pares en la barra, el salto mínimo es de 5kg). Por lo tanto inventé este procedimiento, que está funcionando bárbaro.

Para cada ejercicio:

  1. Siempre hago 3 series.
  2. Y anoto cuanto peso y repeticiones hago en cada serie.
  3. En la última serie de cada ejercicio, trato de hacer todas las repeticiones que pueda (en vez de lo que tenía anotado). Si me excedo por 3 o más de lo que tenía anotado, la próxima hago una repetición más. Sino hago lo mismo que la vez anterior
  4. Si llego a 13 repeticiones (o sea, hago 15 repeticiones en la última serie cuando tenía anotadas 12), en vez de hacer 13 repeticiones la siguiente vez subo el peso lo menos que puedo (5kg en ejercicios con barra, 2.5kg en ejercicios con mancuerna) y hago 8 repeticiones (como tengo que levantar más peso, puedo hacer menos repeticiones).
  5. Cuando el salto es muy grande (por ejemplo, pasar en un ejercicio con mancuernas de 5kg a 7.5kg), en vez de llegar a las 13 repeticiones, trato de llegar a un número más grande (por ejemplo, 20 repeticiones).

 
No sé porqué no te cuenta nadie que ésta es la forma de ir subiendo de peso. Lo que vi en el gimnasio es que te suben los pesos independientemente de como progresás, y entonces o subís demasiado despacio o demasiado rápido y no podés hacer los ejercicios. Me pasó de decirle a un compañero de gimnasio que no tiene que levantar los pesos solo porque lo dice en su ficha. No hagas lo mismo. No te expongas al pedo. Seguí mi procedimiento (o algo parecido).

Y contame como te fue, o si tenés algún truco extra, en los comentarios.

2022-06-27

Montando directorios por ssh

 
Me gusta contectarme a mis VMs por ssh y es bastante cómodo. Pero a veces es un garrón copiar archivos desde/hacia mis VMs. Por eso prefiero montar el disco vía ssh. Saqué cómo hacerlo de acá.

Para hacer eso en ubuntu hay que instalar sshfs

$ sudo apt-get install sshfs

Hacer un directorio vacío para montar el filesystem nuevo.

$ mkdir /home/johndoe/sshfs-path/

Y montar el directorio vía ssh.

$ sshfs  remoteuser@111.222.333.444:/remote/path /home/johndoe/sshfs-path/

Por último, para desmontarlo hay que correr:

$ fusermount -u /home/johndoe/sshfs-path/

2022-04-28

Ignorando .gitignore

 A veces uno quiere ignorar archivos en git pero no quiere tocar .gitignore porque entonces aparece .gitignore como archivo modificado. Y eso es un garrón. Pensé que era inevitable, pero no. Si editan .git/info/exclude, poniendo reglas como las de .gitignore, va a ignorar siguiendo esas reglas en los comandos de git pero no van a ver .gitignore o .git/info/exclude como archivos modificados cuando corran git status (por ejemplo).

Fuente: https://stackoverflow.com/questions/653454/how-do-you-make-git-ignore-files-without-using-gitignore

2022-02-08

Buscando multiplicaciones y divisiones

Desde hace unos meses mi trabajo es hacer auditorías de smart contracts escritos en solidity. Como parte de este trabajo, tengo que buscar multiplicaciones y divisiones en código que no conozco, generalmente usando VSCode.

El tema es que las búsqueda de "*" y "/" dan bocha de falsos positivos porque estos caracteres se usan para marcar comentarios, aparte de las operaciones aritméticas. Por eso uso unas regex para buscarlos, que aunque no son perfectas, me sirven para acotar la cantidad de lugares que tengo que mirar. Quizás les sirvan a ustedes también, así que se las dejo acá abajo:

Multiplicación: [^\s].*[^/*]\*[^/*]

División: [^/*]/[^/*]

Probablemente también puedan serles útiles en otros lenguajes como Python, Java o C++.

2021-09-27

Setupeando hardhat para binance network

Estoy entrando a un laburo nuevo, y como primera tarea tengo que hacer unos POCs de contratos en solidity y testearlos en la red de test de Binance. 

Para hacer esto tengo que hacerlos con hardhat. Por lo tanto empecé a seguir las instrucciones que dan en Binance para setupear hardhat, pero no están del todo bien.

Los problemas que me llevé puestos son:

  • Hay que usar una versión LTS de nodejs. Para eso instalé nvm como dice acá e instalé el LTS usando nvm. La versión que trae ubuntu 20.04 haciendo apt install nodejs es muy vieja.
  • En el archivo hardhat.config.js que ponen ahí, hay que cambiar la versión de solidity a "0.8.4". Sino el contrato de ejemplo que genera cuando corrés npx hardhat no compila.
  • Hay que crear un archivo secrets.json donde poner los mnemonicos que usa metamask. El contenido del archivo es algo así: {"mnemonic": "a ante bajo cabe con contra de desde hacia hasta para por"}

Si el contenido de secrets.json no tiene el formato correcto, o el archivo no existe da un error bastante críptico (Expected a value of type HttpNetworkConfig)

Pero cuando está todo bien configurado pasa esto:

$ npx hardhat compile
Compiling 2 files with 0.8.4
Compilation finished successfully

Espero que les sirva,

Aureliano.

 


2021-08-25

Sistemas egoístas y bienes registrables



Seguramente esto sea un retroplagio, pero estuve notando que muchos sistemas suelen andar con incentivos opuestos, que hacen que llegue a un equilibrio.

Los mercados se basan en que haya compradores y vendedores, con intereses opuestos. El sistema judicial se basa en tener acusación y defensa. Yo creo que esa cualidad de que actores actuando de forma egoísta hacen que salga un resultado bueno es una propiedad deseable de los sistemas.

En particular, hay una aplicación de esto que creo que sería buenísimo hacer y no veo que la hagan. Uno de los problemas que hay con el cobro de impuestos es valuar correctamente los activos sobre los que se aplican. Por ejemplo, el precio de mercado de una propiedad. En Argentina (y creo que en el resto del mundo también) esta valuación se hace con una declaración, por lo que aún cuando se haga bien originalmente, esta valuación se vuelve incorrecta al toque (sobre todo cuando hay mucha inflación o una burbuja inmobiliaria).

Pero esto no tiene porqué ser así. Mi propuesta es usar un "sistema egoísta" para valuar bienes registrables, como por ejemplo terrenos o campos. En este sistema cada dueño declararía el precio de su propiedad, como hasta ahora, y se cobraría un impuesto como porcentaje del valor declarado, como hasta ahora. Lo nuevo es que puede venir cualquier otra persona o empresa y comprar dicha propiedad al doble del valor declarado. Si así lo hiciera, el dueño original se quedaría con el 150% del valor declarado y el 50% restante iría para el estado, en concepto de multa por no declarar correctamente el precio de la propiedad. Esto incentivaría a no sub-declarar el precio de las propiedades.

Algunas cosas que harían andar el sistema, evitando posibles abusos son:

  1. En caso de deber más del 20% del valor declarado de la propiedad en impuestos, la propiedad se subasta, se cobran los impuestos y lo que queda va al propietario.
  2. Para hacer una transferencia hay que llevar a 0 la deuda impositiva de la propiedad.
  3. Para cambiar la valuación de la propiedad hay que llevar a 0 la deuda impositiva de la propiedad.
  4. Para propiedades, no se puede declarar un valor que sea 3 veces superior al valor por metro cuadrado de propiedades similares. Esto es para evitar que declaren un valor muy grande que haga que el 20% del valor declarado sea más que el precio de mercado de la propiedad, haciendo que sea más barato no pagar los impuestos y dejar que la subasten cuando la deuda llegue al límite de subasta.
  5. Hay que poner algún mecanismo sensato de indexación del precio declarado, para evitar que la inflación lo deje muy bajo y haya gente que se aproveche de eso. Quizás ajustar por CER o RIPTE o indice minorista del INDEC por defecto. Este ajuste no requiere llevar la deuda impositiva a 0, y la deuda debería ajustarse por el mismo índice (y quizás aparte algún punitorio para desincentivar tomar la deuda).

 
Creo que si se usara esto para cobrar impuestos a los latifundios de la pampa húmeda, podrían darse las condiciones para que no haya más dólares paralelos ni retenciones a las exportaciones. Y si funciona bien, aplicarla después al resto de las propiedades también.

Espero que alguien tome esta idea y la lleve a cabo.


2021-07-23

Desarrollar con rust en una VM

 En estos días empecé a jugar un poco con rust. Para esto, armé un entorno de desarrollo razonable para mi, en una VM. A mi me gusta armar entornos en VMs para desarrollar así no contamino mi compu con todas las cosas que voy instalando para probar cosas. En particular, prefiero armar VMs de lubuntu (ubuntu con lxde) y laburar conectandome via ssh como si fuera otra compu.

Para esto, armé la VM y le puse 2 placas de red virtuales (una NAT y otra host-only), le instalé lubuntu, le apliqué los últimos paquetes (apt update y apt upgrade) openssh-server (apt install openssh-server), git (apt install git), vscode (snap install --classic code), curl (apt install curl) y rust via rustup (curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh) que incluye cargo.

Para laburar más cómodo le informé mi clave de ssh a la VM usando ssh-copy-id. Después de eso, puedo sshearme a la vm (ssh -X -A aure@192.168.56.101) y operar sin poner passwords y abrir ventanas remotas para usar el vscode (y firefox) de la VM en el escritorio del host.

Para usar firefox hay que correrlo como firefox -no-remote. Para que anden los comandos de cargo y rustup que abren el browser agregué export BROWSER="$HOME/bin/firefox-remote.sh" en el final de mi .profile de la VM. y en $HOME/bin/firefox-remote.sh puse

#!/usr/bin/env bash

firefox -no-remote "$@"

y lo puse como ejecutable con chmod.

En vscode instalé 3 plugins:

  • GitLens para tener mejor soporte para git
  • Rust para tener soporte de la sintaxis de rust
  • CodeLLDB para poder debuggear código de rust dentro de vscode

Con esto creo que estoy listo para empezar a programar dentro de mi VM.

¿Creen que me faltó algo?

2021-04-14

Evitando freezes de la memoria de estado sólido

Tengo una notebook nueva con una ssd kingston de 1 tera, modelo SA2000M81000G según smartctl. Cuando uso la notebook, en forma bastante aleatoria, se pega unos cuelgues de novela. Después de estar como un mes tratando de entender que &&*$#$#&$@#* pasa, creo que encontré la respuesta :D.

Parece que algunas memorias kinsgton tienen un bug que se manifiesta cuando cambian de estado para ahorrar energía. El manejo de energía está en el kernel

La forma de evitarlo es pasarle un parámetro a grub como explican acá.

En mi ubuntu, edité el archivo /etc/default/grub. Cambié la línea GRUB_CMDLINE_LINUX="" para que diga GRUB_CMDLINE_LINUX="nvme_core.default_ps_max_latency_us=0". Después de eso corrí sudo update-grub y reinicié la compu. 

Después de eso, parece que anda todo bien 🤞.


PD: Están trackeando este bug acá

2021-03-05

Memtest Ubuntu booteando desde EFI

Lamentablemente, la opción para correr memtest cuando booteamos no está disponible cuando booteamos de una compu que usa EFI en vez de BIOS.

Por suerte, hay workarround. Casi todos los pasos están documentados acá. La única diferencia para la compu en la que tuve que hacer esto es que, como bootea de memoria de estado sólido, en vez de bootear de /dev/sda4 (o parecido) bootea de /dev/nvme0n1p1, por lo que las instrucciones sobre cómo armar el script para que grub tenga la entrada son un poquitín distintas.

En mi caso el archivo /etc/grub.d/40_memtest es así (en rojo lo que cambié)

#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries.  Simply type the
# menu entries you want to add after this comment.  Be careful not to change
# the 'exec tail' line above.
 
menuentry 'memtest86' {
    insmod part_gpt
    insmod fat
    set root='hd1,gpt1'
    chainloader /EFI/memtest86/BOOTX64.efi
}

No tengo muy claro porqué funciona (fue un SWAG).

Espero que les sirva,

Aureliano.

2021-03-03

pingmon en python3

Hace un tiempo hice un scriptcito en python para monitorear la conexión de red usando ping. Hoy quise usarlo de nuevo y descubrí que no anda en python3, así que lo adapté. Solo necesitaba 2 pequeños cambios:

  • print es python3 es una función, así que tuve que agregar unos paréntesis.
  • sys.stdout.readline() en python 3 devuelve un montón de bytes (en vez de un string) así que agregué .encode() para transformarlo en string

Dejo acá abajo la versión nueva:

#!/usr/bin/env python3

import subprocess
import sys
import re

SEQ_NUMBER_RE = re.compile(".*icmp_seq=(\d+) ttl.*")

def report(probes, count):
  if len(probes) < count:
    return "N/A"
  return str ( count - sum(probes[-count:]) )

def monitor(ip):
  probes = []
  ping = subprocess.Popen(["ping", ip], stdout=subprocess.PIPE)
  stdout = ping.stdout

  stdout.readline() # Ignore first line
  line_count = 0
  last_seq_number = 0
  while True:
    line_count +=1
    seq_number = int( SEQ_NUMBER_RE.match(stdout.readline().decode()).group(1))
    probes.extend([0] * (seq_number - last_seq_number - 1))
    probes.append(1)
    print( "(%s) 10:%s 100:%s 1000:%s" % (
      line_count, report(probes, 10), report(probes, 100), report(probes, 1000)
    ) )

    probes = probes[-1000:] # Keep memory usage bounded
    last_seq_number = seq_number

def main(argv):
  monitor(argv[1])

if __name__ == '__main__':
  main(sys.argv)

 

Happy hacking,
Aureliano

2021-03-02

Deshabilitando touchpad

Mi vieja técnica para deshabilitar el touchpad no anda en lubuntu 20.04. Así que tuve que googlear de nuevo cómo se hace.

Para ver todos los devices de entrada de X-Windows hay que correr 

$ xinput list
⎡ Virtual core pointer                          id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ Telink 2.4G Mouse                         id=11   [slave  pointer  (2)]
⎜   ↳ Telink 2.4G Mouse Consumer Control        id=12   [slave  pointer  (2)]
⎜   ↳ SynPS/2 Synaptics TouchPad                id=16   [slave  pointer  (2)]
⎣ Virtual core keyboard                         id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
    ↳ Power Button                              id=6    [slave  keyboard (3)]
    ↳ Video Bus                                 id=7    [slave  keyboard (3)]
    ↳ Power Button                              id=8    [slave  keyboard (3)]
    ↳ Logitech Logitech G430 Gaming Headset     id=9    [slave  keyboard (3)]
    ↳ Telink 2.4G Mouse System Control          id=10   [slave  keyboard (3)]
    ↳ CHICONY HP Basic USB Keyboard             id=13   [slave  keyboard (3)]
    ↳ HP TrueVision HD Camera: HP Tru           id=14   [slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard              id=15   [slave  keyboard (3)]
    ↳ HP Wireless hotkeys                       id=17   [slave  keyboard (3)]
    ↳ HP WMI hotkeys                            id=18   [slave  keyboard (3)]
    ↳ Telink 2.4G Mouse Consumer Control        id=19   [slave  keyboard (3)]

En mi compu el touchpad tiene el id 16, así que para apagarlo hay que correr xinput --disable "SynPS/2 Synaptics TouchPad" y para prenderlo xinput --enable "SynPS/2 Synaptics TouchPad"

2021-02-16

Porqué no quiero programar en go

Desde que me enteré de su existencia evito programar en go por su manejo de errores, porque te fuerza a implementar a mano el movimiento de los errores por el stack.

Hoy revisé la documentación. Es peor. En el documento que linkié en la oración anterior proponen, para evitar poner ifs por todos lados, seguir ejecutando código después de que los errores suceden. Eso es terrible, lamentable,  parte de la documentación oficial del lenguaje y está incorporado en la biblioteca estándar. 

Eviten go si pueden.

2020-10-01

Problema en VirtualBox después de upgradear host

Estoy usando en mi notebook Lubuntu. Upgradié de 18.04 a 20.04.1 y después de hacer el upgrade y reinstalar en uno de mis guests (que también es LUbuntu 20.04) los guest additions, cuando intento loguearme en la GUI la VM se aborta. Para que esto no pase más, con la VM apagada fui a Settings -> Display -> Graphic Controller y elegí VMSVGA. Después de esto puedo loguearme y el autoresize anda bien :). La idea la saqué de este reporte de bug

 Espero que les sirva!

2020-09-11

Bug en VSCode

En el último año empecé a usar VSCode para programar. Lo uso de una forma extraña, ya que me conecto por ssh a la máquina virtual donde está instalado, forwardeando X11. Aparte lo instalé usando el snap. Hoy lo quise arrancar y me dio una sorpresa fea. En vez de arrancar, solo apareció un rectángulo gris. Me di cuenta que había un problema así que reporté el bug y busqué un workarround. Al final me di cienta que era porque en VSCode upgradearon a Electron 9 y tiene un problema cuando corre con forwardeo de X11 adentro de un container.

Por suerte también hay un workarround. Corrí

export QT_X11_NO_MITSHM=1
en bash antes de arrancarlo y se solucionó el problema.

Update: Si no quieren setear una variable de entorno pueden arrancar VSCode corriendo

code --no-xshm
y también workarroundea el problema.

2020-07-22

Trabajo nuevo, truco nuevo

Ayer me contaron un comando muy cómodo para hacer screenshots en linux. Si corro `scrot -bs` puedo hacer un screenshot de un cacho de la pantalla, que elijo con el mouse.

2020-07-20

Ver uso de disco en linux

Tengo que hacer espacio en mi compu (linux, lubuntu) y quiero ver qué ocupa lugar. Podría empezar a sufrir corriendo `df`, pero hay otra forma :). Corro `sudo ncdu /` y puedo ver qué es lo que ocupa más lugar y borrarlo de forma interactiva desde la línea de comando :). Para instalarlo, correr `sudo apt install ncdu`


2020-04-19

Checklist para entrar a mi casa

Este es el procedimiento que sigo para entrar a mi casa durante la pandemia de COVID-19. Si lo cambio voy a ir editando este post.

También pueden ver el checklist para salir de mi casa.

  1. Desinfectar guantes descartables con alcohol al 70%
  2. Sacarme guantes descartables y tirarlos a la basura
  3. Sacarme zapatillas y medias
  4. Ponerme ojotas
  5. Sacarme antiparras
  6. Sacarme tapabocas y ponerlo en el jarrito 
  7. Desinfectar manos con alcohol al 70%
  8. Poner a hervir con agua el tapabocas
  9. Irme a bañar
  10. Mientras me baño mi novia esteriliza y guarda todas las compras
  11. Me termino de bañar
  12. Me visto
  13. Pongo a lavar en el lavarropas toda la ropa que usé, el tapabocas (que estuvo en agua hirviendo unos 10 minutos), la toalla que usé y cualquier otra cosa que necesite que la lave en el lavarropas
  14. Tiro alcohol al 70% en todos los lugares donde se apoyaron cosas que haya traido de la calle
  15. Cuelgo la ropa

Checklist para salir de mi casa

Este es el procedimiento que sigo para salir de mi casa durante la pandemia de COVID-19. Si lo cambio voy a ir editando este post.

También pueden ver el checklist para entrar a mi casa


Preparación 

Esto se puede hacer con cierta antelación y se puede interrumpir
  1. Armar lista de compras en papel y dejarla en la puerta
  2. Separar plata para compras (y un extra por si aparecen otras cosas para comprar)
  3. Vaciar tachos de basura y dejarlos en la puerta
  4. Reponer bolsas de los tachos de basura
  5. Dejar preparada la ropa para cambiarme cuando vuelva en el baño
  6. Descolgar tapabocas del tendedeor y dejarlo al lado de la puerta
  7. Ponerme ropa para salir a la calle
  8. Acercar gatillo con alcohol al 70% al lado de la puerta de calle

Salida

Esto es cuando salgo, y si se interrumpe hay que empezar desde el principio
  1. Lavarme las manos
  2. Ir a la puerta
  3. Ponerme zapatillas
  4. Desinfectar manos con alcohol 70%
  5. Ponerme tapabocas
  6. Ponerme antiparras
  7. Ponerme guantes descartables
  8. Agarrar basura y changuito
  9. Salir a la calle

2020-03-21

COVID-19

Hace un rato hice un video donde uso un calculador de epidemias para tratar de ver cuáles son las posibles consecuencias del COVID-19, y porqué es tan importante cumplir con la cuarentena y las reglas de higiene (lavarse las manos, no tocarse la cara, etc). Pueden verlo acá abajo:

2020-01-04

Leela agradable

Ultimamente estoy usando leela-zero (con lizzie) para analizar partidas, y está re-bueno. El único problema es que mientras está corriendo el análisis, la compu se trula un poco porque leela-zero y la GUI compiten por la GPU. Para hacer que la GUI ande un poquito mejor, hice un script en bash que hace las veces de "nice" (ya que no hay un equivalente para las GPU de nvidia).

Les dejo el script abajo:

#!/usr/bin/env bash

while true; do
    sudo kill -s STOP $(pgrep leelaz)
    sleep 0.1
    sudo kill -s CONT $(pgrep leelaz)
    sleep 0.2
done


Cambien los tiempos de espera a gusto, y tengan en cuenta que puse sudo porque corro leelaz dentro de un container de docker (no sé si hará falta si lo corren afuera).

Éxitos, y que lleguen a dan pronto!

2019-06-04

Nuevos features de slowbreak

Estuve laburando en slowbreak, mi biblioteca para conectarse a servers de las bolsas de todo el mundo, y tiene algunas mejoras. Acá se las cuento:

  • Utilidades para leer y escribir valores. En el protocolo fix, y slowbreak, el valor de un campo es una tira de bytes. Para hacer más fácil la lectura y escritura de mensajes agregué métodos al módulo message para transformar esos bytes en valores de python. Son estos:
    • message.from_bool/ message.to_bool para leer y escribir booleanos. En fix el valor true se expresa con esta tira de bytes: b'Y'
    • message.from_int/ message.to_int para leer y escribir enteros.
    • message.from_decimal/ message.to_decimal para leer y escribir instancias de decimal.Decimal. Esto es particularmente importante para manejar los precios de las securities con las que operamos.
    • message.from_str(s, encoding='ascii')/ message.to_str(v, encoding='ascii') para leer y escribir strings. En python3 no podemos tratar a los strings como listas de bytes, así que estos métodos se hacen más importantes. Una cosa a notar es que por default usa 'ascii' como encoding. Eso hace que para la mayoría de las cosas ande de una. Si se conectan a un server que manda caracteres fuera del rango, tienen que consultar la documentación sobre el encoding soportado, ya que no está en la especificación de fix (AFAIK).
    • message.timestamp genera los timestamps en el formato adecuado para fix (tal como se usan en los tags 52 y 60). Sin parámetro genera el timestamp actual (GMT). También recibe un objeto datetime si quieren generar un timestamp de otro momento.
  • Confidencialidad de passwords. Los métodos __repr__ y pprint() de message.Message esconden el valor del tag 554 (password). Eso hace que los logs generados por slowbreak no contengan el password utilizado para conectarse al server.
  • Soporte de nombres para las apps. Las apps tienen el parámetro opcional name para darles nombre, que después se puede usar en el método app_by_name para buscar una aplicación hija con el nombre buscado. Esto hace más fácil trabajar con stacks grandes de aplicaciones.
  • Grupos sin limitación de tags. Ahora slowbreak puede parsear grupos aún cuando no sabemos cuáles son todos los tags que pueden ir incluídos. Solo se requiere el counting_tag y el start_tag. Para indicar este modo, pasen el objeto message.AllInclusive en vez de la lista de tags aceptados.
Como siempre, pueden hacer pip install slowbreak para usarlo. Todas las mejoras están en el último release.

¿Querés conectarte a servers de las bolsas del mundo? Puedo ayudarte. Podés contratarme como consultor y te ayudo a usar slowbreak para operar programáticamente.

2019-05-12

Correr lizzie en Ubuntu con GPU

Así hice que corra lizzie en Ubuntu 18.04 con una nVidia GeForge 940mx, compilando y corriendo en un container de docker para que no me ensucie el resto del sistema.

Requisitos:

  • Placa de video nVidia que banque opencl
  • Tener instalado nvidia-docker2 y sus dependencias (incluyendo los drivers correctos).
  • Tener nvidia como engine por default en docker. Para eso hay que agregar "default-runtime": "nvidia" a /etc/docker/daemon.json. Detalles acá. 
Para eso creé un archivo Dockerfile  con los contenidos que explica en esta página:
FROM nvidia/opencl:runtime
RUN apt-get update --no-upgrade -yq && \
    apt-get install --no-upgrade -yq curl git clinfo cmake g++ libboost-dev libboost-program-options-dev libboost-filesystem-dev opencl-headers ocl-icd-libopencl1 ocl-icd-opencl-dev zlib1g-dev qtbase5-dev

RUN mkdir /app && cd /app && \
    git clone https://github.com/leela-zero/leela-zero.git . && \
    git submodule update --init --recursive && \
    mkdir build && cd build && \
    cmake .. && cmake --build . && \
    ./tests && \
    curl -O https://zero.sjeng.org/best-network

WORKDIR /app/build
RUN /app/build/leelaz --tune-only -w best-network
CMD /app/build/leelaz -g -w best-network


Una vez que hice el archivo, corrí:
$ docker build -f Dockerfile -t leela:gpu .

Y para ver que estaba todo bien después corrí la imagen así:
$ docker run --rm -it leela:gpu

Después me bajé lizzie-0.6 y lo corrí con:
$ java -jar lizzie.jar

La primera vez falló, pero generó un archivo de configuración config.txt. Edité ese archivo y cambié la entrada "engine-command" para que diga: "/usr/bin/docker run -i --rm leela:gpu /app/build/leelaz --gtp --lagbuffer 0 -w best-network". Eso hace que lizzie use la leela zero que compilamos antes adentro del docker.

Una vez hecho esto, volví a correr java -jar lizzie.jar y ahora anda :)

Presentación de tito

Ayer hice una presentación de tito el robotito en la Asociación Argentina del juego de Go en el marco del 14o Congreso Argentino de go.

Dejo acá el video por si lo quieren ver:



Y también las referencias que menciono en el último slide:


2019-04-23

Tito el robotito

En las últimas semanas estuve programando un robot que juegue al go usando la misma técnica que AlphaZero. Está escrito en python desde cero, valga la redundancia, y basado en keras y tensorflow.

Hay 2 papers en los que me estuve basando, que son el paper de Alphago Zero y el de Alpha Zero. Ahora estoy entrenándolo, tratando que llegue a jugar un poco mejor que el azar absoluto, en tablero de 9x9 y komi de 5.5. Si quieren mirar cómo está hecho, el código está acá.

La idea atrás de alphago zero es tener (y entrenar) una red neuronal que le das el estado del juego (indicado por las últimas 8 posiciones del juego, para tener en cuenta los kos y triple-kos) y te da 2 resultados: cuán buena es la posición (1 es gano seguro y -1 es pierdo seguro) y para cada jugada posible (o sea, cada intersección del goban) cuanto vale la pena explorar esa jugada para seguir analizando el partido por ahí.

Con esa información, corre un MCTS y busca la jugada que parece mejor. Por supuesto, los detalles están en el paper. En tito, la búsqueda en el árbol está implementada en el módulo tito.alpha.mcts.

Empezando por una red neuronal sin entrenar, se hace que el bot juegue contra sí mismo, después se toman algunas posiciones al azar y sabiendo cómo terminó el partido y cuáles son las posiciones que más exploró se usa esa información para entrenar la red para que la próxima vez que vea esa posición (y similares) de resultados como ese. El código donde hace el entrenamiento está en el módulo tito.alpha.train. Y la red neuronal está en tito.alpha.model.

También, por supuesto, programé las reglas del go, pueden verlo en los módulos, tito.game y tito.board.

Todo esto tiene tests automáticos. En tito.test puse los tests rápidos y en tito.test_slow los que llevan más tiempo.

Para acelerar un poquito esto estoy usando la placa de video de mi compu, así que tuve que hacer andar nvidia-docker y esas cosas. Por suerte la gente de tensorflow tiene imágenes preparadas con todo instalado :D, así que una vez que hacés andar los drivers el resto es fácil.

Por último, aprovecho para contarles que el 11 de mayo de 2019 voy a dar una charla en el Congreso Argentino de Go donde voy a hablar de cómo andan los robots basados en AlphaZero.

2018-11-12

Slowbreak, haciendo una aplicación

Acabo de publicar el tercer video tutorial de slowbreak, donde explico cómo hacer una aplicación que responde a los mensajes que envía el server (AKA: acceptor).

Este tutorial anda con la última versión. Como siempre, para usar slowbreak hacé "pip install slowbreak".



2018-11-08

Segundo tutorial de slowbreak, cómo hacer mensajes de FIX

Update: Saqué al toque la versión 0.0.5, que incluye los ejemplos cuando corrés pip install

Acabo de publicar el segundo tutorial de slowbreak, que explica cómo hacer mensajes fix usando la biblioteca.


También acabo de publicar una nueva versión de slowbreak (la 0.0.4) con soporte para conectarse a servers fix 4.x. Este tutorial anda con la última versión. Como siempre, para usar slowbreak hacé "pip install slowbreak".

2018-11-03

Primer tutorial de slowbreak en video

Acabo de terminar mi primer video-tutorial de slowbreak. Ahí muestro cómo hacemos para conectarnos a un server, usando como server el ejemplo más simple de server que se puede hacer en slowbreak.


También acabo de publicar una nueva versión de slowbreak (la 0.0.3) con soporte para hacer servers (AKA, Acceptors). Este tutorial anda con la última versión. Como siempre, para usar slowbreak hacé "pip install slowbreak".

¡Hasta la próxima!

2018-10-17

slowbreak para python3

Como prometí hace un mes y medio, saqué una nueva versión de slowbreak con soporte para python 3. Anda con python 3.5+, aparte de python 2.7 que sigue andando como siempre.

Para instalarlo solo hay que hacer pip install slowbreak. También estuve documentando un poco y ahora las principales clases y métodos tienen documentación.

Por ejemplo, en el intérprete de python, si hacemos:

>>> from slowbreak.message import Message
>>> help(Message)

Muestra toda la documentación de la clase, explicando cada método y cada parámetro de cada método.

Como siempre, pueden ver el avance del proyecto entrando en la página de bitbucket del proyecto.

Y si se hacen millonari@s con esto, compartan un poquito :D.

2018-09-14

Accediendo a los archivos de mi teléfono

Me compré un celular nuevo (Blu Life One X3) y ya no me aparece el pop-up para copiar archivos a la compu. Para esquivar este problema, voy a la línea de comandos y me instalo jmtpfs. 

sudo apt install jmtpfs

Y creo un directorio para montar el celular
 
sudo mkdir /media/phone

Habiendo hecho esto, cada vez que enchufo el celular a la compu y elijo la opción de "Transfer files", corro

sudo jmtpfs /media/phone -o allow_other

Y en el directorio /media/phone están los archivos del celular
Cuando quiero terminar de usarlo ejecuto.
   
sudo umount /media/phone

Y listo.

2018-09-03

Primer release de slowbreak

Está recién salida del horno slowbreak, la biblioteca que hice para conectarse a servers fix. ¿Qué es fix? Fix es un protocolo que se usa para operar en mercados de acciones, bonos, futuros, opciones, etc.

Ya la usamos para conectarnos a los 2 mercados de la Argentina (BYMA y ROFEX). Por ahora anda solo con python 2.7 (tenemos pensado soportar python 3.x proximamente).

¿Por qué hice una biblioteca de fix cuando existe quickfix?

  1. Porque el binding de python tiene problemas (ejemplo, no se puede iterar por los campos del mensaje) 
  2. Porque para cambiar el tipo de un campo tenés que compilarla a mano, y tocar un .xml horripilante.
  3. Porque la forma de hacer un mensaje fix es demasiado verbose
Ya está publicada en pypi. Para instalarla solo hay que hacer pip install slowbreak.

Prometo ir escribiendo artículos de cómo usar la biblioteca para interactuar con la bolsa. Mientras tanto pueden ver cómo la usamos mirando los tests.

Happy hacking,
Aureliano.

2017-12-03

Mejor pingmon.py

Hace un tiempo hice un script que uso para monitorear el servicio de mi proveedor de internet. Como últimamente estuvo andando mal, lo cambié un toque para que me muestre la cantidad de paquetes perdidos en los últimos 10, 100 y 1000 (en vez de los porcentajes).

Dejó acá el código fuente:

#!/usr/bin/env python

import subprocess
import sys
import re

SEQ_NUMBER_RE = re.compile(".*icmp_seq=(\d+) ttl.*")

def report(probes, count):
  if len(probes) < count:
    return "N/A"
  return str ( count - sum(probes[-count:]) )

def monitor(ip):
  probes = []
  ping = subprocess.Popen(["ping", ip], stdout=subprocess.PIPE)
  stdout = ping.stdout

  stdout.readline() # Ignore first line
  line_count = 0
  last_seq_number = 0
  while True:
    line_count +=1
    seq_number = int( SEQ_NUMBER_RE.match(stdout.readline()).group(1))
    probes.extend([0] * (seq_number - last_seq_number - 1))
    probes.append(1)
    print "(%s) 10:%s 100:%s 1000:%s" % (
      line_count, report(probes, 10), report(probes, 100), report(probes, 1000)
    )

    probes = probes[-1000:] # Keep memory usage bounded
    last_seq_number = seq_number

def main(argv):
  monitor(argv[1])

if __name__ == '__main__':
  main(sys.argv)


Hasta la próxima,
Aureliano.

2017-10-03

Licenciamiento estándar de ideas

Tengo muchas más ideas de las que puedo hacer, y muchas me gustaría que las hagan otras personas. Por eso decidí poner un licenciamiento estándar de mis ideas. Si yo te doy una idea, podés hacer un negocio basándote en ella por una muy pequeña licencia. Los primeros 1e8 dólares que ganes basándote en mi idea son todos tuyos. De lo que quede, dame el 1%. Si no ganás 1e8 dólares, no me debés nada.

PD: El licenciamiento de esta idea aplica a esta idea.

2017-07-03

Go y neurociencia

Hace algunos años que tomé al go (AKA baduk, weiqi) como mi hobbie principal. El go es un juego de mesa de origen chino, de información completa, donde 2 jugador@s se enfrentan intentando rodear la mayor cantidad de territorio. Es un juego que tiene miles de años de historia.
Una de las cosas que más me llamó la atención del go es su elaborado sistema de handicap (AKA, ventaja) que hace posible jugar partidos equilibrados con jugador@s que son mucho mejores o peores que yo. Ese sistema de handicap, también permite medir decaimiento cognitivo. Por ejemplo, yo noto que necesito más ventaja después de un día de programar intensamente. Este sistema de handicap se usa para rankear jugador@s en las diferentes asociaciones de go (amateurs y profesionales) de todo el mundo. Por ejemplo, RANGO es la instanciación de este sistema en la Asociación Argentina del Juego de Go.
¿Y la neurociencia? Ya viene. Este sistema de ranking permite ver bastante claramente cosas que deberían tener una contrapartida en mecanismos cerebrales. Quizás pueda usarse para "ver" como funciona nuestro cerebro. Hay varias cosas que l@s jugador@s de go notamos y que seguramente puedan medirse. Por ejemplo:

  • Algun@s jugador@s suben de nivel de juego y otr@s se quedan clavad@s. ¿Se puede encontrar alguna estructura del cerebro que esté relacionada?
  • Basándonos en la pregunta anterior, ¿es posible encontrar personas que no sepan jugar al go pero que tengan la estructura cerebral correcta y puedan aprender muy rápido?
  • Otra cosa que notamos es que l@s jugador@s de go nos quedamos trabad@s en los mismos rankings cuando vamos aprendiendo. ¿Hay un cambio medible en nuestros cerebros cuando pasamos dichas barreras?
  • ¿El mecanismo que hace que bajemos de nivel cuando estamos cansad@s es el mismo que el que hace que seamos mejores o peores jugador@s en general o no?
Me encantaría saber de investigaciones de neurociencia que intenten contestar éstas u otras preguntas sobre el go. Si las conocen, por favor cítenlas en los comentarios.
Si te dedicás a la neurociencia y querés atacar estos temas, puedo ponerte en contacto con un montón de jugador@s de go e intentar ayudar a armar los experimentos.
Por último, si querés hacer investigación sobre alguna de estas cosas no sientas que este post lo impide. Solamente citame como inspiración y listo :D. Yo no tengo los recursos para hacer la investigación por mi mismo, así que estaría agradecido de que lo haga alguien más.

2017-06-28

Dehabilitar full screen con F11

En lubuntu 16.04, cuando aprieto F11 maximiza la aplicación que estoy usando. Eso es choto porque varios de los programas que uso habitualmente (por ejemplo, eclipse) usan F11 para otras cosas. Por lo tanto decidí deshabilitar esa función. Para eso, edité el archivo ~/.config/openbox/lubuntu-rc.xml, como dice acá

Y después corrí:
$ openbox --reconfigure

Ahora F11 anda como yo quiero :).

PD: Iba a poner los cambios del archivo en este post pero blogger me está haciendo mierda el XML :( 

PD2: En lubuntu 20.04 hay que editar otro archivo (~/.config/openbox/lxqt-rc.xml)

2017-04-17

Fingerprint del certificado

Una de las cosas más molestas de conectarse vía ssl es toda la danza de los certificados, los DNS y las autoridades certificantes. Todo sería mucho más fácil si pudiéramos basarnos en el fingerprint del certificado del server y nos cagáramos en las autoridades certificantes, como hacemos en ssh.

En python podemos hacer eso.

Por ejemplo, para obtener el sha256 del certificado de uno de mis sitios favoritos para jugar al go podemos hacer:

>>> import ssl, socket, hashlib
>>> c = ssl.wrap_socket(socket.socket(socket.AF_INET,socket.SOCK_STREAM))
>>> c.connect(("www.dragongoserver.net",443))
>>> c.do_handshake(True)
hashlib.sha256(c.getpeercert(True)).hexdigest() # da '6cc78bca1293524be3bb0d6321d129d03fd244426c8c3e2ed69f454a14491623' que es el valor del fingerprint SHA 256 del certificado que veo si entro por el navegador al momento de escribir este post.


Con eso, podemos fijarnos si el certificado es el que esperábamos e implementar TOFU ('trust on first use') si nos pinta.

Espero que nos sirva,
Aureliano.

2017-01-13

Windows es simultáneamente fabuloso y horripilante

Como parte de mi trabajo, tengo que trabajar con drivers de Windows; en particular con minifilters. Ayer, mientras estaba tratando de entender cómo anda un minifilter volví a cruzarme con el método FltGetTunneledName mientras navegaba por el API disponible para los minifilters. Cómo no entendía qué catzo es un "tunneled name", googlié un poco y encontré ésta explicación del 2005 en un blog de Microsoft.
Originalmente en DOS, los nombres de archivos tenían hasta 8 caracteres, un punto y 3 caracteres de extensión. Cuando hicieron Windows NT decidieron que querían tener nombres de archivo más copados (parecidos a los de UNIX) y extendieron significativamente el largo posible para los nombres de archivo. El tema es que muchos programas solo sabían manejar nombres cortos. Entonces, para que sigan andando, hicieron que los archivos tuvieran un nombre largo (que es el que realmente querríamos) y uno corto para que estos programas lo usen. También agregaron otras cosas como que los archivos tienen asociada su fecha de creación (información que no estaba disponible en DOS).
El tema es que muchos programas (ej: editores de texto) para grabar un cambio en un archivo (ej: toto.doc) y tener backup de la versión anterior lo que hacen es renombrar el archivo a otro nombre (ej: toto.bak) y después escriben otro archivo con el nombre original. Y hay un problema; si operás solo con los nombres cortos y hacés eso se pierde el nombre largo. Entonces en MS tuvieron la idea de tener un cache de asociaciones de nombres cortos y largos y si creás de nuevo un archivo con un nombre corto que ya usaste le asocia el nombre largo correspondiente (y también su fecha de creación).
Desde el punto de vista del usuario del sistema es fabuloso, ya que un montón de programas que andarían sutilmente mal gracias a este parche andan bien. Pero cuando estás escribiendo drivers de filesystem en el kernel y tenés que lidiar con la complejidad adicional que esto implica, es horripilante.

PD: Acá agrego un artículo que explica con bastante detalle el quilombo agregado por esto a los minifilters.

2017-01-06

Patreon?

Hace un tiempito que estoy subiendo a youtube videos donde juego al go. Hay mucha gente que juega al go mejor que yo y hace videos, así que terminé haciendo personajes mientras juego. Hay varios, algunos son imitaciones de otros youtubers, y otros no.
La gente que hace contenido en forma más o menos recurrente está haciendo cuentas en Patreon para que le paguen. Yo estoy pensando en hacer lo mismo. Y estoy pensando en qué opciones podría poner. Se me ocurren estas:

  • Si gano 10 dólares por mes me compro un micrófono corbatero para que se escuche mejor.
  • Si gano 20 dólares por mes me comprometo a subir un video por semana.
  • Si gano 50 dólares por mes me compro una webcam y filmo los videos mostrando mi cara, y maquillándome como el personaje del video.
  • Si gano 100 dólares por mes pago una conexión de internet como la gente y empiezo a hacer los videos en vivo por Twitch.
  • Si gano 5000 dólares por mes dejo mi laburo y hago videos todos los días.
Para l@s mecenas ofrecería:
  • Si ponés 20 dólares por mes podés jugar contra alguno de mis personajes en un video.
¿Les parece una buena idea?,
Aureliano.

2016-10-31

Pequeño descubrimiento interesante

En la consola de Windows (cmd.exe), si apretás F7 aparece la historia de los comandos de la consola en un hermoso menú, hecho por daltónic@s, que podés manejar con las flechitas (y elegir el comando que quieras). Enter para ejecutarlo, flecha a la derecha para que aparezca escrito en el prompt (sin ejecutar).

2016-08-17

Haciendo un USB booteable para instalar Windows 7

Cuando instalo Ubuntu en una PC nueva, suelo copiar el ISO en un pendrive, haciendo dd. El pendrive tiene que estar enchufado y desmontado, y no queda nada de lo que había grabado antes.

# dd if=/path/to/file.iso of=/dev/sdb bs=4M

Esto no me anduvo para las ISOs de Windows :(. Así que estuve googleando un rato largo hasta que encontré acá instrucciones que logré hacer andar.

Lo que yo hice, basado en lo que linkié arriba es:

  1. Enchufé el pendrive, se puso solo en /dev/sdb (YMMV). El resto de las instrucciones asumen que el pendrive está ahí.
  2. Desmonté el pendrive desde la GUI.
  3. Corrí, sudo cfdisk /dev/sdb y creé adentro una partición NTFS (tipo 7) y la marqué como booteable, asegurándome que tenga suficiente lugar para copiar todo el contenido del ISO de Windows 7.
  4. Formatié la partición, corriendo sudo mkfs.ntfs -f /dev/sdb.
  5. Instalé lilo (con apt-get) y corrí sudo lilo -M /dev/sdb mbr para copiarle un boot record.
  6. Creé en /mnt las carpetas /mnt/iso y /mnt/usb 
  7. Monté por loopback el ISO corriendo sudo mount -o loop win7.iso /mnt/iso.
  8. Monté el pendrive corriendo sudo mount /dev/sdb1 /mnt/usb.
  9. Copié todo el contenido del ISO en el pendrive (usé el Midnight Commander, cp -R debería andar también).
  10. Desmonté iso (sudo umount /mnt/iso) y pendrive (sudo umount /mnt/usb)
  11. Corrí sudo sync para sincronizar los filesystems (creo que no hace falta, pero lo hice igual por las dudas)
  12. Desenchufé el pendrive y lo usé para instalar Windows en otra máquina
Bonus:
Si querés, podés backupear la imagen haciendo sudo dd if=/dev/sdb of=/path/to/image/file.img bs=4M.

Bonus 2:
Si instalaste Windows en una partición, y tenés un Ubuntu en otra, podés seguir estas instrucciones para poder bootear las 2 (mirá la respuesta #6).

Happy hacking!
Aureliano.

2016-07-08

Sandro en otros contextos

Hasta hace 5 minutos sandro sólo andaba si el WAR que deployás lo deployás en el contexto ROOT. Ahora ya no es más así. A partir de ahora es posible correr sandro con toda su magia para compartir código entre cliente y server desde cualquier contexto. Eso permite que haya más de una aplicación de sandro en el mismo Tomcat.

Enjoy!

2016-06-29

Alpha Go, por favor calculá el komi

Hace unos meses Google pateó el goban y mostró que sus computadoras juegan al go mejor que cualquier ser humano. Encima, ¡parece que entrenaron a Alpha Go un poco más y ahora es 4 piedras más fuerte que antes!

O sea, el go dejó de ser algo que nosotr@s hacemos mejor. Pero eso tiene ventajas. Una de las cosas más raras del go es el komi. A diferencia del resto del go, que tiene más de 2000 años, sólo se usa regularmente hace menos de 100 años. El es la ventaja, en puntos, que le da el jugador negro al blanco porque el negro hace la primera jugada. La idea es que esa compensación hace que el partido sea más parejo. Cuando empezaron a usar komi en los torneos profesionales, usaron 4.5 puntos (.5 para evitar empates) y subió a 5.5 y después s 6.5 en Japón y Corea y a 7.5 en China. Pero no sabemos que valor tiene que tener. Y mirar las diferencias de puntos en los partidos de profesionales no sirve porque cambian de estrategia cuando van ganando (y sí, cuentan los puntos en el medio del partido).

Por eso, quiero pedirle a la gente que trabaja en AlphaGo que entrene a AlphaGo con distintos valores de komi y nos cuente, para cada posible valor del komi que porcentaje de las partidas las gana negro en un partido de AlphaGo contra sí mismo. Así podemos elegir el valor de komi que genera menos ventaja.

Si trabajás en DeepMind o conocés a alguien que trabaje ahí, o conocés a alguien que trabaje en Google, o podés contactar a la federación internacional de go, o se te ocurre algún otro camino, por favor pediles que calculen cuál es el mejor komi.

Muchas gracias,
Aureliano.

2016-06-15

Tutorial ctypes

Como parte de mi laburo necesito probar cosas que son exportadas en dlls. Como hacer un programa en C/C++ para probar cada boludez es una patada en los huevos, estoy usando ctypes y scripteando un toque en la consola de python para hacer eso. Y como mi memoria a veces falla, uso este tutorial para que me recuerde cómo hacerlo.

Quizás les sirva a ustedes también,
Aureliano.

2016-05-20

Volviendo a C++

En mi laburo necesitan que programe en C++ nuevamente, así que hace unos días estoy mirando y recordando C++. ¡Qué incómodo que es!

Default constructors, copy constructors, asignment operators, que encima en su versión generada por default andan mal. Métodos virtuales, ¿por qué hay otra opción, y encima no es el default? ¿Herencia virtual? ¿Functors o function pointers? ¿Punteros comunes, shared pointers, referencias? ¿Cómo obtengo el shared_pointer de this? ¿Y si hay herencia? ¿Templates? Encima los templates son simultáneamente complicadísimos y sumamente limitados. Por ejemplo, ¿cómo hacés un template que cambie el nombre de los métodos generados en función de algún parámetro? Creo que no se puede (si sabés cómo hacerlo sin usar #define decime). Escribir todo varias veces, forward declarations, declaraciones en el .h e implementaciones, con la signatura repetida, en el .cpp.

Y ni hablar de los quilombos asociados, sobre todo en Windows, que no son del lenguaje, pero que te complican la vida. Cambiás de compilador y linkea mal y, en el caso de Visual Studio también genera binarios incompatibles según el runtime que elegís. De regalo el Visual Studio que estoy usando no se da cuenta cuándo recompilar y genera mierda desactualizada.

Pero eso no es lo peor de C++. Para mi lo peor de C++ es que no te deja pensar. Tenés que esquivar tanta complejidad accidental que el código fuente no reflejo de las ideas subyacentes. Eso nos devalúa como programadores.

2016-05-01

Mejorando pingmon.py

En mi post anterior puse un scriptcito en python para monitorear una conexión con ping. Después de eso lo mejoré un toquecito, y ahora toma los últimos 10, 100 y 1000 probes y calcula el porcentaje que tienen respuesta. La ejecución se ve así:
$ pingmon.py 8.8.8.8
(1) 10:N/A 100:N/A 1000:N/A
(2) 10:N/A 100:N/A 1000:N/A
(3) 10:N/A 100:N/A 1000:N/A
(4) 10:N/A 100:N/A 1000:N/A
(5) 10:N/A 100:N/A 1000:N/A
(6) 10:N/A 100:N/A 1000:N/A
(7) 10:N/A 100:N/A 1000:N/A
(8) 10:N/A 100:N/A 1000:N/A
(9) 10:N/A 100:N/A 1000:N/A
(10) 10:N/A 100:N/A 1000:N/A
(11) 10:100.00% 100:N/A 1000:N/A
(12) 10:100.00% 100:N/A 1000:N/A
(13) 10:100.00% 100:N/A 1000:N/A


Y el código del script creció un poquito, pero sigue entrando en un post.
#!/usr/bin/env python

import subprocess
import sys
import re

SEQ_NUMBER_RE = re.compile(".*icmp_seq=(\d+) ttl.*")

def pct_str(probes, count):
  if len(probes) < (count + 1):
    return "N/A"
  return "%.2f%%" % ( 100 * sum(probes[-count-1:-1]) / count )

def monitor(ip):
  probes = []
  ping = subprocess.Popen(["ping", ip], stdout=subprocess.PIPE)
  stdout = ping.stdout

  stdout.readline() # Ignore first line
  line_count = 0
  last_seq_number = 0
  while True:
    line_count +=1
    seq_number = int( SEQ_NUMBER_RE.match(stdout.readline()).group(1))
    probes.extend([0] * (seq_number - last_seq_number - 1))
    probes.append(1)
    print "(%s) 10:%s 100:%s 1000:%s" % (
      line_count, pct_str(probes, 10), pct_str(probes, 100), pct_str(probes, 1000)
    )

    probes = probes[-1000:] # Keep memory usage bounded
    last_seq_number = seq_number

def main(argv):
  monitor(argv[1])


Siéntase a gusto de hackear este script si quieren algún monitoreo distinto (cambiar la salida y/o la forma de monitorear).

Espero que les sea útil,
Aureliano.

2016-04-28

Monitoreo con ping

A veces mi proveedor de internet anda mal. Una de las cosas que pasan es que empieza a dropear muchos paquetes y, para ver eso, hago un ping. El problema es que el ping no te dice el porcentaje de los paquetes que se cayeron :/. Por lo tanto hice un script en python, al que llamé pingmon.py, que wrappea ping y da esa información en cada vuelta exitosa de un paquete.

La salida se ve así:
$ pingmon.py 8.8.8.8
Packets returned: 1 Packet count: 1 Return percentage: 100.00 %
Packets returned: 2 Packet count: 4 Return percentage: 50.00 %
Packets returned: 3 Packet count: 5 Return percentage: 60.00 %
Packets returned: 4 Packet count: 7 Return percentage: 57.14 %
Packets returned: 5 Packet count: 8 Return percentage: 62.50 %
Packets returned: 6 Packet count: 11 Return percentage: 54.55 %


Y el código del script es éste:
import subprocess
import sys
import re

SEQ_NUMBER_RE = re.compile(".*icmp_seq=(\d+) ttl.*")
 

def monitor(ip):
  ping = subprocess.Popen(["ping", ip], stdout=subprocess.PIPE)
  stdout = ping.stdout

  stdout.readline() # Ignore first line
  line_count = 0
  while True:
    line_count +=1
    seq_number = int( SEQ_NUMBER_RE.match(stdout.readline()).group(1))
    print "Packets returned: %d Packet count: %d Return percentage: %.2f %%" % (line_count, seq_number, (100.0 * line_count)/seq_number )

def main(argv):
  monitor(argv[1])


Espero que les sirva,
Aureliano.

2016-02-11

ZTE MF110 en lubuntu 14.04

Por cuestiones laborales, tengo que hacer andar un modem 3G en Linux. Después de bucear por mercado libre, encontré un ZTE MF 110, al que le puse un chip de datos de Claro. Usándolo desde una VM con Windows 7 en VirtualBox y haciendo pass-through para que maneje el pituto usb, anduvo joya. Pero en Linux, andaba intermitentemente. Estuve googleando y descubrí que muchos MODEMs 3G andan en 2 modos. Un modo que es una unidad de mass-storage, donde tiene drivers y manuales para Windows, y otro que es el modem en sí. Y linux, en teoría, cambia de modo automáticamente con el usb_modeswitch. Bueno, cuestión que en lubuntu 14.04 está roto ese cambio, porque falla el montado de la unidad de mass-storage y aborta el proceso. Si corren

$ dmesg | tail -n 15

después de enchufar el modem van a ver una línea como esta mostrando el error.

[ 8905.896992] systemd-udevd[6462]: Failed to apply ACL on /dev/sr0: No such file or directory

Parece que udev y este modem no se llevan bien. Si miramos usando lsusb se ve que está en modo mass-storage:

$ lsusb | grep -i zte
Bus 003 Device 037: ID 19d2:2000 ZTE WCDMA Technologies MSM MF627/MF628/MF628+/MF636+ HSDPA/HSUPA


Seguí googleando y encontré cómo hacer el switch del modo manualmente en este post. Entonces ejecuté:

$ sudo usb_modeswitch -v 19d2 -p 0x2000 -K
[sudo] password for aure:
Look for default devices ...
   product ID matched
 Found devices in default mode (1)
Access device 035 on bus 003
Current configuration number is 1
Use interface number 0
Use endpoints 0x01 (out) and 0x81 (in)

USB description data (for identification)
-------------------------
Manufacturer: ZTE,Incorporated
     Product: ZTE Configuration
  Serial No.: A SERIAL NUMBER
-------------------------
Sending standard EJECT sequence
Looking for active driver ...
 OK, driver detached
Set up interface 0
Use endpoint 0x01 for message sending ...
Trying to send message 1 to endpoint 0x01 ...
 OK, message successfully sent
Read the response to message 1 (CSW) ...
 Response reading failed (error -7)
 Device is gone, skip any further commands
-> Run lsusb to note any changes. Bye!


Uso lsusb de nuevo y ahora cambió el device.

$ lsusb | grep -i zte
Bus 003 Device 038: ID 19d2:0031 ZTE WCDMA Technologies MSM MF110/MF627/MF636


Mirando en dmesg aparece un cartel que dice que lo reconoció como modem:

[ 9919.553348] usb 3-3: GSM modem (1-port) converter now attached to ttyUSB2

Y después apareció la opción de activar "Mobile broadband" en el iconito de red :D

Happy hacking,
Aureliano.

2016-02-08

Regalos de carnaval para sandro

Hice 2 arreglos en sandro.

  • Cambié la forma de usar rhino y ahora puede procesar archivos grandes de javascript de nuevo. Eso hace que ande d3 en el server nuevamente.
  • Ahora los archivos .js que usa sandro tienen que estar encodeados en utf-8, permitiendo escribir en el código cosas como ( ͡° ͜ʖ ͡°).
Happy hacking,
Aureliano.

2016-01-31

Ciclo completo para autenticarse por oauth2 en línea de comando

Hace un tiempito que estoy jugando con el API de blogger para hacer posts automaticamente. Lo que me parece más complicado es autenticarse vía oauth2 desde una aplicación de línea de comando. Por eso hice un módulo de sandro que lo hace por mi, pidiendo el código de autorización solo cuando es necesario

Se puede usar como módulo de sandro así:

define({
  accessToken: './accessToken'
},
function(m) {
  var accessToken = m.accessToken(
    'refresh_token_fname.txt', 
    {
      clientId: 'CLIENT ID HERE',
      clientSecret: 'CLIENT SECRET HERE'
    }
  )
  // Use accessToken here!
})

Abajo les dejo accessToken.js completo (cuidado, son 134 líneas de código):

define({
  javaPackages: "javaPackages",
  ary: "sandro/nadaMas/array",
  json: "sandro/nadaMas/json",
  object: "sandro/nadaMas/object"
}, function(m) {
 
  var URL = m.javaPackages.java.net.URL
  var String = m.javaPackages.java.lang.String
  var URLEncoder = m.javaPackages.java.net.URLEncoder
  var StandardCharsets = m.javaPackages.java.nio.charset.StandardCharsets
  var InputStreamReader = m.javaPackages.java.io.InputStreamReader
  var BufferedReader = m.javaPackages.java.io.BufferedReader
  var Scanner = m.javaPackages.java.util.Scanner
  var System = m.javaPackages.java.lang.System
  var File = java.io.File
  var FileOutputStream = java.io.FileOutputStream
  var OutputStreamWriter = java.io.OutputStreamWriter
  var out = System.out
  var in_ = new Scanner(System["in"])

  var paramBytes = function(p) {
    var s = new String( Object.keys(p).map(function(k) {
      return "" + URLEncoder.encode(k, "UTF-8") + "=" + URLEncoder.encode(p[k])
    }).join("&") )
    return s.getBytes(StandardCharsets.UTF_8)
  }
 
  var postJsonResult = function(url, params) {
    var parameters = paramBytes(params)
    var c = new URL(url).openConnection()
    try {
    c.requestMethod = "POST"
    c.doOutput = true
    c.fixedLengthStreamingMode = parameters.length
    c.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
    c.connect()
    c.outputStream.write(parameters)
  
    if (200 != c.responseCode) {
      return null
    }
    var response = ""
    var reader = new BufferedReader( new InputStreamReader( c.inputStream ) )
  
    while( true ) {
      var buff = reader.readLine()
      if (buff == null) {
        break
      }
      response += buff
    }
    return m.json.parse( "" + response )
    } finally {
      c.disconnect()
    }
  
  }
  var authCode2refreshToken = function(authCode, credentials) {
    var rv = postJsonResult(
      "https://www.googleapis.com/oauth2/v4/token", {
        code: authCode,
        redirect_uri: "urn:ietf:wg:oauth:2.0:oob",
        client_id: credentials.clientId,
        client_secret: credentials.clientSecret,
        grant_type: "authorization_code"
      }
    )
  
    return rv ? rv["refresh_token"] : null
  }
 
  var refreshAccessToken = function(refreshToken, credentials) {
    var rv = postJsonResult(
      "https://www.googleapis.com/oauth2/v4/token", {
        refresh_token: refreshToken,
        client_id: credentials.clientId,
        client_secret: credentials.clientSecret,
        grant_type: "refresh_token"
      }
    )
 
    return rv ? rv["access_token"] : null
  }
 
  var readFileLine = function(fname) {
    var sc = new Scanner(new File(fname))
    try {
      return sc.nextLine()
    } finally {
      sc.close()
    }
  }
 
  var writeFileLine = function(fname, line) {
    try {
      var w = new OutputStreamWriter( new FileOutputStream(fname) )
      line = "" + line
      w.write(line, 0, line.length)
    } finally {
      w.close()
    }
  }
 
  var obtainAccessToken = function(refreshTokenFname, credentials) {
    var accessToken = null
    var refreshToken = null
    try {
      refreshToken = readFileLine(refreshTokenFname)
    } catch (e) {
      // ignore
    }
  
    while (true) {
      if (refreshToken) {
        accessToken = refreshAccessToken(refreshToken, credentials)
      }
      if (accessToken) {
        return accessToken
      }
    
      var authorizationUrl =
        "https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/blogger&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=" +
        credentials.clientId
      out.println("Anda a este URL, completa la autorizacion y pega el codigo de autorizacion aca. Despues presiona ENTER")
      out.println(authorizationUrl)
      var authCode = in_.nextLine()
      refreshToken = authCode2refreshToken(authCode, credentials)
      writeFileLine(refreshTokenFname, refreshToken)
    }
  }
 
  return obtainAccessToken
})



Si quieren, es bastante fácil transliterarlo a código Java.

Espero que les sirva,
Aureliano.

2016-01-30

Obteniendo el access token

Sigo en mi cruzada para hacer cosas automáticamente en blogger usando sandro. En este caso, hice el código para obtener un access token a partir de un authorization code. Para eso, primero necesitás registrar una aplicación de línea de comando. Una vez que lo hayas hecho, vas a tener el client id y el client secret. Pero eso no alcanza, para obtener el access token, como el que usamos en el post anterior, hace falta un authorization code. Para obtenerlo, conectate a este URL:

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/blogger&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=ACA_VA_TU_CLIENT_ID

Y te va a mostrar el authorization code después de loguearte a google y autorizar a tu aplicación a hacer cosas de blogger en tu nombre.

Una vez que obtuviste tu authorization code, hay que obtener un access token. El access token te sirve para manipular blogger con tu usuario por la próxima hora. Para renovarlo, hay que usar un refresh token, que también obtendremos, pero voy a mostrar cómo usarlo en otro post.

Acá abajo dejo el código de ejemplo para obtener estos tokens.

define({
  javaPackages: "javaPackages",
  ary: "sandro/nadaMas/array",

  json: "sandro/nadaMas/json",
  object: "sandro/nadaMas/object"
}, function(m) {
 
  var URL = m.javaPackages.java.net.URL
  var out = m.javaPackages.java.lang.System.out
  var String = m.javaPackages.java.lang.String
  var URLEncoder = m.javaPackages.java.net.URLEncoder
  var StandardCharsets = m.javaPackages.java.nio.charset.StandardCharsets
  var InputStreamReader = m.javaPackages.java.io.InputStreamReader
  var BufferedReader = m.javaPackages.java.io.BufferedReader
 
  var tokenUrl = new URL("https://www.googleapis.com/oauth2/v3/token")

  var paramBytes = function(p) {
    var s = Object.keys(p).map(function(k) {
      return "" + URLEncoder.encode(k, "UTF-8") + "=" + URLEncoder.encode(p[k])
    }).join("&")
    out.println(s)
    s = new String(s)
    return s.getBytes(StandardCharsets.UTF_8)
  
  }
 
  return function(code, client_id, client_secret) {
    var parameters = paramBytes({
      code: code,
      redirect_uri: "urn:ietf:wg:oauth:2.0:oob",
      client_id: client_id,
      client_secret: client_secret,
      grant_type: "authorization_code"
    })
    var c = tokenUrl.openConnection()
    c.requestMethod = "POST"
    c.doOutput = true
    c.fixedLengthStreamingMode = parameters.length
    c.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
    c.connect()
    c.outputStream.write(parameters)
  
    var response = ""
    var reader = new BufferedReader( new InputStreamReader( c.inputStream ) )
  
    while( true ) {
      var buff = reader.readLine()
      if (buff == null) {
        break
      }
      response += buff
    }
    var jsonResponse = m.json.parse( "" + response )

    out.println("Tokens: " + m.json.stringify(jsonResponse))

  }
})


No uso la google-client-api de java porque es un quilombo y no entendí cómo usarla, así que me conecto a los webservices de google directamente.