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.


2016-01-24

Accediendo al API de blogger desde sandro

Hoy estuve jugando con el API de blogger desde un script que hice usando sandro. Como puedo usar las APIs de java desde el código JavaScript que implementa los módulos de Sandro, decidí usar la biblioteca que google provee para Java.
Lo que más me costó fue toda la parte de oauth2. Como estoy solo probando, no quise armar todo un webserver para hacer todo el camino de oauth. En vez de eso, usé el oauth playground para obtener un access-token y programé desde ahí.

Con todo esto, y afanando bastante de acá hice un scriptcito que obtiene algunos datos de este blog.

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

}, function(m) {
 
  var Blogger = m.javaPackages.com.google.api.services.blogger.Blogger
  var NetHttpTransport = m.javaPackages.com.google.api.client.http.javanet.NetHttpTransport
  var JacksonFactory = com.google.api.client.json.jackson2.JacksonFactory
  var GoogleCredential = com.google.api.client.googleapis.auth.oauth2.GoogleCredential

  var HTTP_TRANSPORT = new NetHttpTransport();
  var JSON_FACTORY = new JacksonFactory();
  var CREDENTIAL = GoogleCredential().setAccessToken("HERE GOES THE ACCESS TOKEN")
 
  var buildBlogger = function() {
    var bb = new Blogger.Builder(HTTP_TRANSPORT, JSON_FACTORY, CREDENTIAL)
    bb.applicationName = "Testing blogger"
    return bb.build()
  }

  return function() {
    var out = m.javaPackages.java.lang.System.out
   
    var blogger = buildBlogger()
   
    var action = blogger.blogs().getByUrl("http://aurelianito.blogspot.com")
 

    action.fields = "description,name,posts/totalItems,updated"

    var blog = action.execute();

    // Now we can navigate the response.
    out.println("Name: " + blog.name)
    out.println("Description: " + blog.description)
    out.println("Post Count: " + blog.posts.totalItems)
    out.println("Last Updated: " + blog.updated)
   
  }

})


Si lo corren debería mostrarles algo muy parecido a:

Name: aurelianito
Description: Blog de Aureliano Calvo.
Aureliano Calvo's blog.
Post Count: 318
Last Updated: 2016-01-20T19:49:14.000-03:00


Después tengo que ver cómo hacer para hacer que el flujo del oauth ande bien.

Happy hacking,
Aureliano