martes, 14 de diciembre de 2010

Barajita premiada (#20) - seq con formato

Típico que necesita sacar por consola una secuencia de números, entonces utiliza el comando seq, por ejemplo:
$ seq 1 5 
1
2
3
4
5

O necesita esos números de dos en dos:
$ seq 1 2 5
1
3
5

La cosa se pone interesante cuando requiere formatos, por ejemplo obtener siempre números de dos cifras, entonces puede utilizar:
$ seq -f "%02g" 1 5
01
02
03
04
05

A este punto usted se preguntará, ¿y para qué puede serme útil esto? Pues yo no lo sé, no mentira, esto podría resultar útil para -por ejemplo- filtrar entradas de un log de apache donde cada línea lleva algo como:
192.168.0.1 - - [01/Nov/2010:00:00:33 +0100] "GET ..." 

Si quisiera extraer cada entrada para un día específico podría utilizar un script del tipo:
#!/bin/bash

for day in `seq -f "%02g" 1 31`; do
  count=`egrep -i "\[$day/Nov/[0-9:+ ]+\] \"GET $line" logs/*access.$month.log | grep " 200 " | wc -l`
  echo "$day,$line: $count"
done

¿Y para qué sirvió el seq? para hacer un "for" con valores de dos dígitos y que así tuviera sentido el grep que hacemos dentro del búcle (ver: $day).


martes, 23 de noviembre de 2010

Buscando campos únicos en archivos separados por caracteres (Ej. CSV)

El comando uniq me devuelve, para un conjunto generalmente ordenado previamente (ver: sort), un listado de líneas únicas. Inclusive me puede ayudar a contar el número de coincidencias.

¿El problema? Cuando tengo un archivo con campos separados por caracteres, como un CSV, ¿cómo hago para pedirle sólo únicos para determinados campos del archivo? Pues como Master Card, para todo lo demás AWK ;-)

$ cat a
1|1|2
1|2|3
1|1|5
2|6|7
$ awk -F '|' '!x[$1]++' a
1|1|2
2|6|7
$ awk -F '|' '!x[$1,$2]++' a
1|1|2
1|2|3
2|6|7

Fíjese que para el archivo "a" con campos separados por tuberías "|" extraigo los caracteres únicos, primero considerando el primer campo y luego considerando los primeros dos, Esto no podría hacerse fácilmente con uniq porque tendría que utilizar la vaga y precaria función -skip-chars.


lunes, 25 de octubre de 2010

Transformando un archivo de ids en X para reemplazar en queries del tipo SELECT ... WHERE id IN (X)

Otra útil... típico que tenemos un archivo con un montón de ids y queremos meterlos dentro de alguna query del tipo "WHERE field IN (X)", donde X está formado por todos los ids. Este comando retorna X.

$ awk -v _SQ="'" 'NR>1 { retorno = retorno ", " _SQ $0 _SQ } END { print retorno }' ids 

El archivo ids contiene un id por línea.

* Este ejemplo siempre agrega un id vacio ('') al comienzo, para quitarlo basta agregar un IF. Yo lo he dejado porque me conviene en la mayoría de casos.


Cómo enviar la salida de una query a un archivo separado por "comas" (mysql)

Esta es sencilla pero útil, cómo ejecutar una query y enviar su salida a un archivo separado por comas (o cualquier otro caracter). En PostgreSQL es trivial, pero en MySQL no existe (o al menos no lo he conseguido) un parámetro que defina un FS (field separator).

$ mysql -u root db_name < query | sed 's/\t/|/g' | tee output.csv
Dentro del archivo query está la QUERY. La salida, producto de ejecutar la query, la procesamos con sed reemplazando TABS (\t) por PIPES* (|).

* En lugar de PIPES podrían ser comas, pero no recomiendo ese caracter porque es muy común.

martes, 12 de octubre de 2010

Barajita premiada (#19) - cut & paste

Estos dos comandos son súper útiles cuando deseamos generar archivos CSV a partir de otros CSV. Sobre todo cuando necesitamos pegar columnas con otras (verticalmente). Por ejemplo:

rodolfo@rcampos-laptop:~/cutAndPaste$ cat a 
id|nombre|apellido|nombreCompleto
1|Rodolfo|Campos|Rodolfo Campos
2|Juan|Pérez|Juan Pérez
rodolfo@rcampos-laptop:~/cutAndPaste$ cat b
id|cédula
1|12345678
2|87654321
rodolfo@rcampos-laptop:~/cutAndPaste$ cut -d'|' -f2 b | paste -d '|' a -
id|nombre|apellido|nombreCompleto|cédula
1|Rodolfo|Campos|Rodolfo Campos|12345678
2|Juan|Pérez|Juan Pérez|87654321

En el ejemplo mostrado arriba hacemos lo siguiente:
  1. Cortamos la segunda columna del archivo "b" (opción -f), especificando el delimitador (-d).
  2. Pegamos la salida después del contenido de "a" y la separación de ambos archivos es marcada con un "|" (opción -d). Note que la salida que viene de la tubería (del comando anterior) se especifica como entrada del comando paste utilizando un guión.


Barajita premiada (#18) - sort

Sort, tan "sencillo" pero tan "poderoso".

El comando sort permite ordenar líneas de un flujo de bytes. Este ordenamiento se puede hacer considerando caracteres ASCII, numéricos, binarios, entre otros.

Por ejemplo:
rodolfo@rcampos-laptop:~/sort$ cat a
hello
1
3
44
4
5
foo
rodolfo@rcampos-laptop:~/sort$ sort a
1
3
4
44
5
foo
hello
rodolfo@rcampos-laptop:~/sort$ sort -n a
foo
hello
1
3
4
5
44

En el ejemplo mostrado arriba, el archivo "a" fue ordenado en orden alfabético y luego numérico (opción -n). Para ambos casos, puede observar la diferencia por las posiciones de las palabras y los números 4, 5 y 44.

Pero sort también puede ordenar un archivo con líneas con campos separados por algún caracter (Ej. CSV) rápidamente y sin mayores complicaciones. Esto sobre todo me fue de gran utilidad para ordenar un archivo que pesaba 1Gb y estaba separado por "|". Obviamente debido al tamaño, no resultaba trivial abrir el mismo con Excel y pedirle que ordenara las líneas por la columna X. Mire este ejemplo:

rodolfo@rcampos-laptop:~/sort$ cat a
name|id|type
first|3|1
second|1|1
third|2|2
fourth|0|2
rodolfo@rcampos-laptop:~/sort$ sed '1d' a | sort -t '|' -nk 2
fourth|0|2
second|1|1
third|2|2
first|3|1

En el ejemplo de arriba, es removida la primera línea del archivo con sed (1d) y luego ordenado de forma numérica (-n) considerando el segundo campo (-k 2) para un flujo con campos separados por "|" (opción -t).


jueves, 19 de agosto de 2010

Barajitas premiadas (#17) - encoding

Encodings, encodings, problemáticos todo lo que tenga que ver con ellos...

Típico que tenemos un archivo en ASCII y lo necesitamos en UTF-8, o UTF-16 o ISO8859-X, etc, etc.

Aquí les dejo 3 métodos que pueden utilizar para cambiar el encoding de un archivo:
  1. Abriendo el archivo con un bloc de notas (Ej. gedit) y seleccionando explícitamente el encoding (codificación) del archivo al "guardar como..."
  2. Utilizando el comando iconv, Ej. iconv -f ASCII -t UTF-8 -o UTF_FILE ASCII_FILE
  3. Utilizando VIM (en modo comando con el archivo abierto), la secuencia:
    :set bomb
    :set fileencoding=utf-8
    :wq


De todas las formas mencionadas especialmente me gusta la tercera. Y luego para verificar si todo se hizo: file IS_IT_REALLY_AN_UTF_FILE

Replicación Maestro->Esclavo1->Esclavo2 en mySQL

A continuación les dejo una chuleta (cheat sheet) para configurar bases de datos mySQL de forma Maestro->Esclavo1->Esclavo2.

Configuración inicial

Maestro: host localhost, port 17050
Esclavo1: host localhost, port 17051
Esclavo2: host localhost, port 17052

Configurando Maestro-Esclavo1

Primero: Configuración del Maestro
  1. Debe tener log-bin (bajo [mysqld] en my.cnf)
  2. Debe tener un usuario para replicación
    GRANT REPLICATION SLAVE ON *.* TO 'repl'@'localhost' IDENTIFIED BY '123456';
  3. Debe extraer los parámetros de configuración para Esclavo1
    SHOW MASTER STATUS;

Segundo: Configuración del Esclavo1
  1. Debe tener log-bin (bajo [mysqld] en my.cnf)
  2. Debe tener log-slave-updates (bajo [mysqld] en my.cnf)
  3. Debe tener un usuario para replicación
    GRANT REPLICATION SLAVE ON *.* TO 'repl'@'localhost' IDENTIFIED BY '123456';
  4. Debe configurar Esclavo1 como esclavo de Maestro, para lo cual deberá introducir los parámetros obtenidos arriba en el paso 3.
    CHANGE MASTER TO MASTER_HOST='localhost', MASTER_PORT=17050, MASTER_USER='repl', MASTER_PASSWORD='123456', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=202
  5. Debe comenzar la replicación
    START SLAVE;

Incorporando al Esclavo2

Una vez que el esquema Maestro-Esclavo1 se encuentre funcionando correctamente, se procederá a configurar el Esclavo2.

Primero: Extracción del dump (a partir de Esclavo1)
  1. Debe detener la replica como esclavo y liberar sus logs (en Esclavo1):
    STOP SLAVE;
    FLUSH LOGS;
  2. Debe extraer un dump (de Esclavo1)
    mysqldump -u root --password=msandbox --port=17051 --host=127.0.0.1 --master-data=2 test > dump1.sql
  3. Reiniciar la replica como esclavo (en Esclavo1 - volviendo a la normalidad):
    START SLAVE;

Segundo: Configuración del Esclavo2
  1. Restaurar el dump (en Esclavo2)
    mysql -u root --password=msandbox --port=17051 --host=127.0.0.1 test < dump1.sql
  2. Extraer parámetros de configuración del maestro. Para ello deberá buscar en el dump (dump1.sql) una línea como la siguiente:
    -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=106
  3. Configurar Esclavo2 como esclavo de Esclavo1. Deberá copiar los parámetros extraidos en el paso 2 e introducirlos en este comando:
    CHANGE MASTER TO MASTER_HOST='localhost', MASTER_PORT=17051, MASTER_USER='repl', MASTER_PASSWORD='123456', MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=106
  4. Iniciar el esclavo (en Esclavo2)
    START SLAVE;

NOTA: La configuración inicial Maestro-Esclavo se asume desde el inicio, antes de que la base de datos tuviese registro alguno.

Para este ejercicio utilicé MySQL Sandbox

Y para realizar inserciones -sin parar- en las tablas del maestro utilicé el siguiente script:

#!/bin/bash

x=0;     # initialize x to 0
#while [ "$x" -le 10 ]; do
while true; do
    mysql -u root --password=msandbox --port=17050 --host=127.0.0.1 -e "INSERT INTO test.a VALUES($x)"   
    # increment the value of x:
    x=$(expr $x + 1)
    sleep 2
done

OJO: La tabla que uso es muy sencilla: CREATE TABLE a (a int); dentro del esquema test que instala por defecto MySQL Sandbox.


jueves, 12 de agosto de 2010

Cómo escapar caracteres para manipularlos en una consola

Esto me dio bastantes problemas la verdad. Tenía un archivo con comandos SQL y otras cosas, entonces era común conseguir comillas simples y dobles por todos lados. Por cuestiones que no vienen al caso, necesitaba manipular cada línea con funciones AWK; el cual colapsaba con todos estos caracteres especiales.

Aquí la solución, primero el comando que me hizo ver la luz:

$ echo $(printf '%q' $line)

La función mostrada arriba escapa todos los caracteres especiales. Ahora sólo me quedaba depurar las líneas del archivo y ejecutar un comando para cada una de ellas:

#!/bin/bash
cat PROBLEMATIC_FILE |
while read line; do
  echo $(printf '%q' $line) |
  awk -v _SQ="'" '{
   # ... process
    print $0
  }' 
done 


miércoles, 11 de agosto de 2010

Cómo calcular el md5 para cada línea de un flujo de bytes

Necesitaba calcular el md5 para cada línea de un flujo de bytes y así obtener un identificador; claro, siempre y cuando siempre existiese algo que hacía cada línea diferente.

Pensé en el md5 de cada línea. Aquí les dejo la barajita...

$ cat FILE | awk -v _SQ="'" '{ system("echo " _SQ $0 _SQ " | md5sum") }'

Enjoy!

PD. Si se fijan, podría decidir que partes de cada línea consideraré para mi identificador... utilizando $1, $2, $3, etc... Este es realmente el sentido de esta barajita... porque sino podría obviar el AWK.


martes, 10 de agosto de 2010

Cómo obtener los privilegios (grants) de todos los usuarios de una BD MySQL

MySQL ofrece un comando para ver los privilegios (grants - o más bien concesiones) de un usuario, éste es: show grants for user@machine. Recuerde que el comodín para máquinas es: %.

Luego, me preguntaba cómo hacer para obtener todos los privilegios para todos los usuarios y de una vez en un formato que pudiese llevar a otro manejador (para restaurar), pues casualmente conseguí el dato aquí y lo comparto con ustedes porque lo considero de gran utilidad.

$  mysql -u root --batch --skip-column-names -e "SELECT user, host FROM user" mysql | sed 's,\t,"@",g;s,^,show grants for ",g;s,$,";,g;' | mysql --batch --skip-column-names | sed 's,$,;,g'

La salida estará lista para ingresarla en otro manejador. Ahora, si lo que se desea es simplemente ver el desglose de los permisos de un usuario, puede ejecutar:
$ mysql -u root -e "SELECT * FROM information_schema.USER_PRIVILEGES"


domingo, 8 de agosto de 2010

Barajitas premiadas (#16) - shell scripts y php

Los shell scripts son súper útiles. Luego, hay veces en las que necesitamos apoyarnos en algún otro lenguaje script para realizar ciertas tareas, entre los más comunes: perl, python y ruby.

Como he trabajado bastante con PHP y me gusta este lenguaje, pues lo prefiero sobre los mencionados anteriormente. Aquí les dejo un ejemplo de PHP y BASH donde muestro algo de aritmética de fechas, espero sea de utilidad!

date1='2010-08-01'
date2='2010-08-20'

result=`echo '<?php
  $date = "'$date1' - 2 day";
  $dateTime = new DateTime($date);
  echo $dateTime->format("Y-m-d");
?>' | php` 

echo "$date1 - 2 day = $result"

result=`echo '<?php
  $date = "'$date1' + 2 week 4 hour 2 min 1 sec";
  $dateTime = new DateTime($date);
  echo $dateTime->format("Y-m-d H:i:s");
?>' | php` 

echo "$date1 + 2 week 4 hour 2 min 1 sec = $result"

result=`echo '<?php
  $date1 = "'$date1'";
  $date2 = "'$date2'";
  $days = (strtotime($date2) - strtotime($date1)) / (60 * 60 * 24); // msec(*60)->sec(*60)->hour(*24)->day
  echo $days;
?>' | php` 

echo "$date2 - $date1 = $result days"


sábado, 7 de agosto de 2010

Ejecutando comandos sobre un programa en ejecución y capturando sus salidas (en PHP)

En este artículo les dejo un código que me causo varios dolores de cabeza, para el que tuve que hacer un montón de pruebas hasta lograrlo.

Para mi proyecto phpautoconf estaba necesitando, en PHP, abrir un proceso (programa en ejecución), ejecutar comandos y tras cada uno, capturar su salida. Parece sencillo, pero el problema es que PHP por defecto maneja los flujos de forma bloqueante, según lo explicado aquí, e intentar manejarlos de forma diferente requiere de algunas "cosillas interesantes".

La solución:

<?php
// file: proc.php

// proc_open parameters
$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from    1 => array("pipe", "w"),  // stdout is a pipe that the child will write to    2 => array("pipe", "w") // stderr is a file to write to
); $cwd = '/tmp'; $process = proc_open('bash', $descriptorspec, $pipes, $cwd); // stream_select parameters (waits until something can be read from pipe) $read = array($pipes[1]); $write = null; $except = null; $readTimeout = 5; if (is_resource($process)) { stream_set_blocking($pipes[1], FALSE); fwrite($pipes[0], "echo 'hola'\n"); fflush($pipes[0]); stream_select($read, $write, $except, $readTimeout); $output = fgets($pipes[1]); echo "output = $output\n"; fwrite($pipes[0], "echo 'chao'\n"); fflush($pipes[0]); stream_select($read, $write, $except, $readTimeout); $output = fgets($pipes[1]); echo "output = $output\n"; fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); $return_value = proc_close($process); echo "return_value = $return_value\n"; } ?>

Las "cosillas interensantes":
  1. stream_set_blocking($pipes[1], FALSE);. Define la tubería de salida como no bloqueante. En términos prácticos, permitirá obtener la salida sin necesidad de cerrar el flujo (fclose).
  2. fflush($pipes[0]). Libera el flujo de entrada (flush).
  3. \n al final de cada comando escrito (fwrite). Le indica al proceso, en este caso la consola bash, que finalizó el comando.

Como dato interesante, conseguí en varios foros que mucha gente utilizaba la función sleep entre escritura y lectura para dar tiempo a que se llenara el flujo de salida. Sugiero mejor utilizar la función stream_select, que espera un máximo de tiempo por la respuesta, pero si ésta se recibe antes, se libera el bloqueo. Para ejecutar:

$ php proc.php


jueves, 5 de agosto de 2010

Cambiando fechas en ficheros

Este artículo va de algo que tuve que resolver hoy... Tenemos un proceso que genera archivos con fecha, del tipo: archivo_YYYYMMDD.gz. El problema: estos archivos debían tener la fecha del día anterior.

Me explico mejor, el siguiente archivo: mi_archivo_20100805.gz, debía llamarse en realidad: mi_archivo_20100804.gz, como pueden ver debía restar 1 al día que era impreso como fecha y cambiar el nombre de cada archivo.

Inicialmente ataqué el problema con AWK, pero me di por vencido cuando me enfrenté a los archivos con día 01, no podía colocar día 00, debía ser el último día del mes anterior y con esto, otro de los más grandes problemas... Qué mes? año bisiesto? etc, etc...

Entonces, obviamente necesitaba de algún lenguaje de programación que me permitiera hacer operaciones aritméticas con fechas, por ejemplo: PHP.

Aquí les dejo el script que creé, espero sea de utilidad para alguien:

# file: replace_name_-1day.sh
#!/bin/bash

if [ $# -lt 3 ] # validating parameters
then
  echo "Usage: replace_name_-1day.sh prefix date dir"
  echo "date - must be in Y-m-d format"
  exit
fi

prefix=$1
date=$2
dir=$3

fDate=`echo '<?php 
  $date = "'$date'";
  $dateTime = new DateTime($date);
  echo $dateTime->format("Ymd");
?>' | php` # file actual date

fNewDate=`echo '<?php 
  $date = "'$date' - 1 day";
  $dateTime = new DateTime($date);
  echo $dateTime->format("Ymd");
?>' | php` # file new date

mv $dir/$prefix\_$fDate.xml.gz $dir/$prefix\_$fNewDate.xml.gz

Para correr esto pueden seguir el siguiente ejemplo:
$ date=`date +%Y-%m-%d` # sets today's date in format YYYYMMDD
$ dateFile=`date +%Y%m%d` # sets today's date in format YYYY-MM-DD
$ touch mio_$dateFile.gz # generates an empy file with the specified name
$ ./replace_name_-1day.sh mio $date dir 

Se preguntarán por qué manejar la fecha con diferentes formatos (20100805 ó 2010-08-05), pues resulta que es un parámetro que necesita otro programa que no está en este artículo...



miércoles, 4 de agosto de 2010

Buscar y reemplazar una cadena de caracteres por otra

Buscar y reemplazar una cadena de caracteres (string) por otra es pan nuestro de cada día!

La cuestión es cuando tenemos algún archivo muy grande, de esos que no podemos abrir ni siquiera con un bloc de notas (unos 2GB?) y deseamos realizar esta operación... Alguien familiarizado con sistemas *nix podría decir... Ah, pero abro el archivo con VI y listo... Quisiera ver si tenemos tanta suerte trabajando en un computador de sólo 1GB de RAM y abriendo un archivo de unos 10GB. Claro siempre podríamos tirar a la papelera ese viejo computador y comprar otro ;D

Pero eso no es necesario, siempre podemos valernos de una de las mejores herramientas que *nix ha parido, SED (Stream EDitor). Este programa toma un flujo de salida y lo procesa, línea por línea, según alguna tarea definida.

Por ejemplo, si deseo reemplazar en un archivo todas las coincidencias de la cadena "NOTEPAD mola" por "mi NOTEPAD no mola tanto", puedo utilizar el siguiente comando:

$ sed 's:NOTEPAD mola:mi NOTEPAD no mola tanto:g' MI_ARCHIVO

Fíjese que utilizo ":" para separar: el comando "s" (busca y reemplaza), de la cadena a buscar (NOTEPAD mola), de la cadena que utilizaremos para reemplazar las coincidencias (mi NOTEPAD no mola tanto) y el comando "g" (global - para todas las coincidencias en la línea). NOTA: Puedo reeemplazar los ":" por "/".


martes, 3 de agosto de 2010

Barajitas premiadas (#15) - ssh proxy

Si tenemos acceso restringido en nuestra máquina hacia la Internet, pero tenemos acceso SSH a otra máquina que no tiene estas restricciones, o estamos detrás de alguna red y queremos acceder a otra... Siempre podemos iniciar un túnel SSH que nos sirva como suerte de Proxy (utilizando la opción -D).

Voy a dar un ejemplo concreto... Hay una máquina que tiene un servidor Apache HTTP en una red diferente a la mia que ofrece un recurso que deseo, como tengo acceso SSH a una máquina con vista a ambas redes, puedo iniciar un túnel:

$ ssh -D 1234 user@server

Esto definirá un túnel (SOCKS) en mi máquina a través del puerto 1234, al que puedo conectarme para tener acceso a la otra red... Entonces, coloco en mi navegador Web como proxy SOCKS (máquina: localhost, puerto: 1234) y listo... Algo como http://DIRECCION_IP_OTRA_RED/RECURSO tendría sentido.


Barajitas premiadas (#14) - bc

Aquí les dejo un método veloz para convertir números en notación decimal a binaria, octal o hexadecimal... (También puede utilizar gcalctool para hacer esto en modo gráfico)

Vamos a convertir 192 (decimal) a binario, octal y hexadecimal...
$ echo 'obase=2;192' | bc
$ echo 'obase=8;192' | bc
$ echo 'obase=16;192' | bc

Puede hacer la operación inversa con la opción ibase (Ej. ibase=2;1010 => 10).


lunes, 2 de agosto de 2010

domingo, 1 de agosto de 2010

Procesando los archivos allCountries.txt (ciudades) y countryInfo.txt (países) de GeoNames

Hace unos días les dejé una barajita en la que comentaba de dónde se pueden sacar ciudades y países del mundo en un formato actualizado. Ver el siguiente enlace.

Estuve intentando llevar esta info a mi BD. Descargué del siguiente enlace los archivos allCountries.zip y countryInfo.txt que contienen las ciudades y países respectivamente.

El problema se presentó al llevar esta info a una BD, debía procesar los archivos, que tienen un montón de data que no me interesaba, y convertirla a queries.

Aquí les dejo un script que creé que extrae información de ambos archivos y genera los scripts city.sql y country.sql para PostgreSQL.

#!/bin/bash

####################
# List interest fields (cities - city.sql)
# $1 - geonameid         : integer id of record in geonames database
# $2 - name              : name of geographical point (utf8) varchar(200)
# $9 - country code      : ISO-3166 2-letter country code, 2 characters
# $18 - timezone          : the timezone id (see file timeZone.txt)

# DDL
echo "CREATE TABLE city(geonameid int, name varchar(200), country character(2), timezone varchar(100));" > city.sql
echo >> city.sql 

# INSERTS
# 1. Search ' and replace for ''
# 2. Construct the query (look field separator \t and single quote variable _SQ)
sed "s:':'':g" allCountries.txt | awk -F "\t" -v _SQ="'" '{
  print "INSERT INTO city(geonameid, name, country, timezone) VALUES(" \
  $1 ", " _SQ $2 _SQ ", " _SQ $9 _SQ ", " _SQ $18 _SQ ");"
}' >> city.sql


####################
# List interest fields (countries - country.sql)
# $1 - ISO
# $5 - Country
# $6 - Capital
# $9 - Continent
# $16 - Languages
# $17 - geonameid

# DDL
echo "CREATE TABLE country(iso character(2), country varchar(200), capital varchar(200), continent character(2), languages varchar(200), geonameid int);" > country.sql
echo >> country.sql

# INSERTS
# 1. Discarting rows that begins with #
# 2. Search ' and replace for ''
# 3. Construct the query (look field separator \t and single quote variable _SQ)

grep -v "^#" countryInfo.txt | sed "s:':'':g" | awk -F "\t" -v _SQ="'" '{
  print "INSERT INTO country(iso, country, capital, continent, languages, geonameid) VALUES(" \
  _SQ $1 _SQ ", " _SQ $5 _SQ ", " _SQ $6 _SQ ", " _SQ $9 _SQ ", " _SQ $16 _SQ ", " $17 ");"
}' >> country.sql 

####################
# List continents

# grep -v "^#" countryInfo.txt | awk -F "\t" '{ print $9 }' | sort | uniq -d 

Se puede ver que el script se vale de las herramientas por excelencia para trabajar con flujos de datos en *nix, a saber: GREP, SED y AWK. Espero sacar varias barajitas de este mismo artículo, como hice con Chat p2p Netcat cifrado con OpenSSL.

De momento creo que es importante mencionar que los archivos fuente poseen la info en líneas, donde cada campo es separado por un TAB (\t). Para procesar esto utilicé AWK y para cada línea extraigo el campo de interés a través de su índice (su posición en la línea según cada TAB). Es muy común realizar procesos como este valiéndose de hojas de cálculo (Ej. Excel)... Se coloca toda la info en una hoja de cálculo y luego se arman los queries utilizando la función de concatenación. El problema está cuando se tiene un archivo muy pesado (como en este caso setecientos y pico de megas) y al abrir el mismo con Excel o Calc, el computador se cuelga.


sábado, 31 de julio de 2010

Barajitas premiadas (#13) - MySQL Sandbox

Esta herramienta me parece una excelente alternativa para hacer pruebas con MySQL. Les cuento para qué la utilicé para que se hagan una idea de su utilidad.

Por cuestiones de trabajo necesitaba hacer pruebas con varias bases de datos MySQL, esto porque tenemos un entorno con replicación maestro-esclavo bien diverso.

Primero pensé en montar varias máquinas virtuales en mi computador -con Ubuntu- pero éste tiene serias limitaciones de memoria y procesamiento, por lo que descarté VMWare y VirtualBox. Luego pensé en instalar una máquina virtual más liviana que compartiera funcionalidades del kernel, algo tipo Zonas de Solaris, conseguí Linux-VServer pero lo encontré bastante limitante, tuve que recurrir a un montón de artilugios con IPTables para lograr que medio funcionara.

Entonces... Pensé en instalar varias instancias de la base de datos en mi máquina... Cuando comencé a configurar cada una comenzó el karma, por lo engorroso de sus archivos de configuración. Y de repente, se hizo la luz! Conseguí el MySQL Sandbox y listo!

Esta herramienta permite configurar tantas instancias de MySQL como se desee a partir de sus binarios. Por ejemplo:

$ make_sandbox mysql.tar.gz
$ ~/sandbox/MY_SANDBOX/start_all
$ mysql -u root --password=msandbox -h localhost --port=17050 &
$ mysql -u root --password=msandbox -h localhost --port=17051 &
$ mysql -u root --password=msandbox -h localhost --port=17051 &

Enjoy! Siempre puede ver los archivos de configuración de cada instancia (my.cnf) dentro de las carpetas correspondientes a cada nodo, dentro del sandbox.


PHP Auto Conf

Perdonen que he andado un poco perdido con el blog, he estado full con el desarrollo de una herramienta para automatizar tareas utilizando scripts escritos en PHP, la he llamado phpautoconf.

Pretendo que facilite la automatización de tareas, sobre todo para sysadmins. Espero escribirles pronto al respecto, de momento comenzaré a colocar todo en GitHub; ya les avisaré!

jueves, 29 de julio de 2010

Cómo hacer que un script se ejecute al iniciar/reiniciar/apagar el sistema

Este artículo me parece que explica de forma sucinta y excelente cómo hacer que un script se ejecute al iniciar/reiniciar/apagar el sistema. Además, siguiendo el método mencionado dejaremos un script en init.d que podría ser utilizado durante una sesión para iniciar o detener el servicio.

Making scripts run at boot time with Debian

lunes, 26 de julio de 2010

Barajitas premiadas (#12) - tail

Del artículo Chat p2p Netcat cifrado con OpenSSL espero sacar varias barajitas...

La primera, tiene que ver con el comando tail. Este comando permite ver las últimas líneas de un archivo. Por ejemplo, a continuación se mostrarán las últimas 20 líneas del archivo de logs del sistema; muy útil para obtener información cuando se adjunta algún equipo al computador (ver también dmesg).

# tail -20 /var/log/messages

Ahora, este comando posee una opción poderosa que permite ver el contenido de un archivo en línea, de forma sigilosa (opción -f), es decir, conforme este cambie se irán mostrando sus registros... Esto es súper útil para chequear logs del sistema (Ej. logs de un servidor HTTP).

#  tail -f /var/log/apache2/error.log

Y para finalizar el artículo, hay veces en las que deseo realizar una tarea para cada nuevo registro escuchado por el tail, para ello puedo utilizar el comando read dentro de un bucle de la siguiente forma:

#  tail -f /var/log/apache2/error.log | while read -r line; do php procesador.php $line; done

El comando mostrado arriba ejecutará el script procesador.php para cada nueva línea dentro del archivo /var/log/apache2/error.log. Podríamos suponer que el script se conecta a una BD y deja el nuevo registro en ella.


domingo, 25 de julio de 2010

Chat p2p Netcat cifrado con OpenSSL

Este artículo lo escribí pensando en incluir un canal cifrado para el chat p2p que comenté días atrás en: La navaja suiza.

Quería incluir un cifrado sencillo simétrico (utilizando DES) con OpenSSL y además quería hacerlo para cada entrada enviada entre Cliente y Servidor y no por todo un bloque.

Como me gustó tanto este ejercicio, creé los siguientes scripts (que de preferencia recomiendo ejecutar en el orden en que son presentados):
  1. start_server.sh Inicia el netcat como escucha y envía la salida a un archivo (OUTPUT_FILE). Podrá notar que siempre que se corre este script, es vaciado el contenido del OUTPUT_FILE
  2. start_client.sh Comienza la lectura sigilosa de un archivo (de tipo PIPE*) utilizando el comando tail -f. Cada nueva entrada en este archivo (INPUT_FILE) será enviada al servidor haciendo uso de netcat.
  3. receiver.sh Este script y el mencionado seguidamente, son los encargados de tomar las entradas o salidas de netcat y cifrarlas o descifrarlas. En este caso, este script toma lo que ha recibido el servidor (OUTPUT_FILE) y lo descifra (puesto que dentro del mismo quedará el contenido cifrado)
  4. send.sh Este script permite enviar un mensaje desde el cliente al servidor, para lo cual utiliza el archivo INPUT_FILE, donde coloca el mensaje cifrado.

*: En el siguiente enlace podrá conseguir información bastante útil acerca de archivos FIFO.

En definitiva, los scripts start_server.sh y start_client.sh establecen la conexión TCP a través de netcat. Luego, los scripts send.sh y receiver.sh cifran y descifran los mensajes que viajarán entre cliente y servidor. Para utilizar estos scripts correctamente, deberá tener en el cliente los archivos start_client.sh y send.sh, y en el servidor, los archivos start_server.sh y receiver.sh.

Los scripts:

Script: start_server.sh
#!/bin/bash
OUTPUT_FILE='output'
PORT='1234'

if [ $# -eq 2 ]
then
        OUTPUT_FILE=$1
        PORT=$2
elif [ $# -gt 0 ]
then
        echo "Usage: start_server.sh [output_file port]"
        exit
fi

cat /dev/null > $OUTPUT_FILE && netcat -l -p $PORT > $OUTPUT_FILE

Script: start_client.sh.
#!/bin/bash
SERVER='localhost'
PORT='1234'
INPUT_FILE='input'

if [ $# -gt 3 ]
then
        echo "Usage: start_client.sh [server] [port] [input_file]"
        exit
else
        if [ $1 ]; then SERVER=$1; fi
        if [ $2 ]; then PORT=$2; fi
        if [ $3 ]; then INPUT_FILE=$3; fi
fi

if [ -p $INPUT_FILE ]
then
        tail -f $INPUT_FILE | netcat $SERVER $PORT
else
        echo "$INPUT_FILE isn't a pipe file. First, you may want to run: mkfifo $INPUT_FILE"
fi

Script: receiver.sh
#!/bin/bash
PASSWORD='1234'
OUTPUT_FILE='output'

if [ $# -gt 2 ]
then
        echo "Usage: receiver.sh [output_file] [password]"
        exit
else
        if [ $1 ]; then PASSWORD=$1; fi
        if [ $2 ]; then OUTPUT_FILE=$2; fi
fi

tail -f $OUTPUT_FILE | \
        while read -r line
        do
                echo $line | openssl enc -d -a -des3 -pass pass:$PASSWORD
        done

Script: send.sh
#!/bin/bash
PASSWORD='1234'
INPUT_FILE='input'

if [ $# -lt 1 ] || [ $# -gt 3 ]
then
        echo "Usage: send.sh message [password] [input_file]"
        exit
else
        if [ "$1" ]; then MESSAGE=$1; fi
        if [ $2 ]; then PASSWORD=$2; fi
        if [ $3 ]; then INPUT_FILE=$3; fi
fi

if [ -p $INPUT_FILE ]
then
        echo $MESSAGE | openssl enc -e -a -salt -des3 -pass pass:$PASSWORD -out $INPUT_FILE
else
        echo "$INPUT_FILE isn't a pipe file. Probably you don't have a server_client running"
fi

Un ejemplo:

En una consola (Consola #1 - Servidor):
$ ./start_server.sh &
$ ./receiver.sh

En otra consola (Consola #2 - Cliente):
$ mkfifo input
$ ./start_client.sh &
$ ./send.sh "primer envío"



viernes, 23 de julio de 2010

Barajita premiada (#11) - ejecutar un comando para cada línea de un archivo

Típico que tenemos en algún archivo un listado de nombres y queremos ejecutar un comando para cada una de las líneas del mismo. Por ejemplo: supongamos que tengo un directorio con un montón de archivos, pero quiero borrar sólo un subconjunto de los contenidos en éste. Entonces, podría copiar los nombres (uno por línea) dentro de un archivo y pedir que se ejecute el comando rm por cada línea... Esto no tiene sentido si el subconjunto está compuesto por 5 nombres o menos, pero si son más, seguro que sería de utilidad algo como:

$ for i in `seq 1 100`; do touch $i; done # generando archivos vacios
$ cat a_borrar # mostrando el archivo que contiene la lista de elementos a borrar
22
3
4
65
6
12
45
90
32
76
48
98
32
$ for i in `cat a_borrar`; do rm $i; done # borrando los archivos especificados



miércoles, 21 de julio de 2010

Barajitas premiadas (#10) - countries, states, cities

No puedo dejar pasar esta barajita porque precisamente estaba buscando al respecto y conseguí unos excelentes enlaces, y antes de que se me olvide...

Típico de un sistema... dos o tres combos dependientes uno del otro donde se cargarán paises, estados y ciudades (Ej. Fulano vive en Venezuela->Miranda->San Antonio de Los Altos) ¿el problema? los datos...

De dónde saco los datos más actualizados, pues por supuesto que ya mucha gente tuvo el mismo problema. Aquí les dejo los dos vínculos que encontré mejor, el primero el oficial y el segundo (extra-oficial) sencillo, pero con estados sólo de EEUU:


Están en inglés, pero a final de cuentas si queremos algo internacionalizable (el común hoy día), guste o no, esa es la segunda lengua por excelencia.

Barajitas premiadas (#9) - netcat

Supongamos que necesita una conexión telnet (sobre Windows) o rsh (sobre Linux) y no tiene estas herramientas a la mano o por alguna razón no desea utilizarlas (muchas razones me vienen a la mente, pero me las reservo porque este artículo tiene fines pedagógicos, o en todo caso de white hacking, y no vandálicos).

Pues, también puede utilizar necat para esto y listo; abordamos la herramienta en el artículo anterior.

La idea es dejar a netcat escuchando donde desea iniciar la consola remota y especificar que inicie un intérprete de comandos cuando alguien inicie una conexión por ese puerto.

En la máquina remota escucharía de la siguiente forma:
$ netcat -l -p 1234 -e /bin/bash

Y luego en la máquina local lo siguiente sería totalmente válido:
$ netcat localhost 1234
ls / #este comando se ejecutará remotamente (abajo se muestra la salida)
bin
boot
cdrom
dev
etc
home
initrd.img
lib
lost+found
media
mnt
opt
proc
root
sbin
selinux
srv
sys
tmp
usr
var
vmlinuz

Ahora, en el momento en que cierre el cliente, el netcat que corre del lado del servidor se cerrará. Para evitar esto, podemos correrlo dentro de un ciclo infinito, algo como:
$ while true; do netcat -l -p 1234 -e /bin/bash; done

Ahhhhh y a este punto usted se preguntará, ¿y cómo hago para cifrar el canal? para ello OpenSSL vendría de perlas, esa la dejo para otro artículo!


La navaja suiza

Netcat es sin duda una herramienta indispensable para cualquier sysadmin. Los soldados del ejercito suizo no van a la guerra sin su navaja, por qué usted habría de olvidarla...

Es una herramienta que permite escribir y leer datos a través de la red - sobre el protocolo TCP/IP. Proximamente espero escribir algunos artículos con ejemplos varios al respecto.

Usted puede hacer que netcat escuche en un determinado puerto TCP utilizando la opción -l. Entonces, hay un ejemplillo -que siempre llevé conmigo a clase mientras estuve de profe- que permite estudiar: qué envían los navegadores a los servidores en la red cuando hacen peticiones HTTP; por supuesto que podría encender algún sniffer y verlo, pero con netcat me parece algo más pedagógico.

Primero que nada instale netcat con APT, YUM, o directamente con un RPM, DEB, compilándolo, en fín, como desee. Luego, puede establecer un chat punto a punto sencillo:

$ netcat -l -p 1234 #esto en una consola
$ necat localhost 1234 #esto en otra consola, luego escriba y ENTER (vea las salidas en cada una de las consolas)

Ahora a lo nuestro, lo que quería demostrar de las peticiones HTTP. La idea es dejar al necat escuchando en un puerto, configurar el navegador Web para que utilice como proxy nuestro socket de netcat y luego hacer una petición... Verá como se genera la salida; que puede almacenar en un archivo y luego utilizarla (esto lo explicaremos en otro artículo).

La configuración del proxy...



Netcat escuchando y su salida en pantalla luego de realizar -con el navegador- una petición a http://netcat.sourceforge.net/...
$ netcat -l -p 1234

GET http://www.google-analytics.com/__utm.gif?utmwv=1.3&utmn=968922939&utmcs=ISO-8859-1&utmsr=1280x800&utmsc=24-bit&utmul=es-ar&utmje=0&utmfl=10.0%20r22&utmdt=The%20GNU%20Netcat%20--%20Official%20homepage&utmhn=netcat.sourceforge.net&utmhid=1586803424&utmr=-&utmp=/&utmac=UA-398133-1&utmcc=__utma%3D100602113.1703963196.1279727538.1279727538.1279727538.1%3B%2B__utmz%3D100602113.1279727538.1.1.utmccn%3D(organic)%7Cutmcsr%3Dgoogle%7Cutmctr%3Dnetcat%7Cutmcmd%3Dorganic%3B%2B HTTP/1.1
Host: www.google-analytics.com
User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.9.0.11) Gecko/2009061212 Iceweasel/3.0.6 (Debian-3.0.6-3)
Accept: image/png,image/*;q=0.8,*/*;q=0.5
Accept-Language: en-us,es-ve;q=0.7,fr;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://netcat.sourceforge.net/

Fíjese en el detalle de la salida... La petición (el sitio utiliza Google Analitycs para llevar las estadísticas de uso; y quién no lo hace hoy día!!!) mis lenguages configurados, encoding, entre otros.




martes, 20 de julio de 2010

Barajitas premiadas (#8) - SSH port forwarding

La barajita de hoy tiene un valor sentimental importante para mi... jejejeje

Esta sí que la he utilizado bastante y no me canso de bendecirla. La explico con un ejemplo para que se entienda más fácil...

Imagine que está en un entorno de desarrollo donde usted tiene su máquina y accesso vía SSH al servidor de Calidad. Este servidor está protegido con un firewall y no puede acceder desde su máquina a otro puerto del mismo, que no sea el 22 (puerto SSH por omisión).

La cuestión surge cuando usted, que tiene acceso vía SSH, desea conectarse a la BD PostgreSQL que está corriendo en ese servidor desde su máquina, usted necesita acceder al puerto 5432 (puerto PostgreSQL por omisión) y sabe que cuando está conectado al servidor de forma local tiene acceso, pero de forma remota no. Entonces...

Usted puede crear un tunel (además sobre SSH - cifrado) y establecer una correlación entre un puerto local y el puerto remoto!! Creada la correlación, se conectaría a la BD como de forma local, pero realmente SSH estaría enviando toda la info al puerto en la máquina remota... qué fácil ah? Veamos cómo podría resolver el problema que he comentado.

$ ssh -NL 1234:localhost:5432 a@example.com & 
$ psql -h localhost -p 1234 mydb myuser

El primer comando, crea una conexión SSH entre los puertos local 1234 y remoto 5432 (opción -L) para el usuario a del servidor (example.com). Utilizo la opción -N para que no sea iniciada una consola remota y &.para que el proceso quede en background.

Luego, inicio una conexión con la BD como si fuese local para el usuario myuser y la BD mydb.


SOA y Symfony

Cuando hablamos de Arquitecturas Orientadas a Servicio (SOA - por sus siglas en inglés) es común pensar en Servicios Web y todo lo que conllevan. Mucha gente comienza metiendo Servicios Web por todos lados...

Yo prefiero pensar en arquitecturas donde la lógica de negocio se encuentra perfectamente desacoplada y que luego puede ser aprovechada parte de ésta a través de, por ejemplo SOAP, valiéndose de una fachada.

Para la gente que programa en Java es común pensar en algo como: peticiones HTTP que toma algún framework MVC (Ej. Struts), que delega en Servicios que llaman DAOs, que a su vez se apoyan en un ORM (Ej. Hibernate)... Algo por el estilo, y bueno por supuesto inversión de control (IoC - por sus siglas en inglés) de Spring.

Luego, Symfony ofrece un manejo excepcional MVC, pero... Mucha gente, sobre todo al utilizar formularios olvida la capa de servicios y adiós SOA. A continuación les presento un ejemplo sencillo al respecto:

Primero: Creando la clase de servicio

De momento defineremos una clase Usuario de servicio con un método eliminar (simulado):

//file: /lib/service/user.class.php

class UserService {
 //...
 
 public static function remove($id) {
  //TODO elimina un usuario de BD según el id dado y en caso de error arroja una excepción
 }
 //...
}

Fíjese que el archivo fue colocado dentro de la carpeta lib del proyecto, por ello, Symfony la cargará automáticamente por usted. Además, el método remove es estático porque éste no requiere del estado de un "posible" objeto; generalmente los métodos de servicio son estáticos, puesto que la lógica desarrollada en ellos no requiere estado.

Segundo: El controlador

El código del controlador o lógica de presentación (como prefiero llamarla) deberá colocarlo dentro de su acción, por ejemplo:

//file: /apps/myapp/modules/user/actions/actions.class.php

class userActions extends sfActions
{
 //...

 public function executeRemove(sfWebRequest $request)
 {
  $id = $request->getParameter('id');

  //TODO aquí deberá realizar todas las validaciones pertinentes

  try {
   UserService::remove($id);

   //Manejar el caso de éxito
  } catch (MyCustomException $mce) {
   //TODO manejar la excepción
  }

  //...
 }

 //...

Fíjese que para invocar el método remove de la clase UserService no necesité hacer ningún include o algo parecido. El día de mañana si necesito exponer este método como servicio, simplemente haría una fachada, por ejemplo:

Tercero: La fachada

//file: /lib/soap/user.class.php

class UserSOAP
{
 //...

 public function remove($id)
 {
  //TODO aquí deberá realizar todas las validaciones pertinentes

  try {
   UserService::remove($id);

   //Manejar el caso de éxito
  } catch (MyCustomException $mce) {
   //TODO manejar la excepción
  }

  //...
 }

 //...

Por supuesto que debería utilizar el paquete SOAP de PEAR o algún plugin de Symfony (Ej. ckWebServicePlugin) para que efectivamente esta fachada sea invocada al llamar un Servicio Web.

IMPORTANTE: Sobre todo debe notar que en el execute de mi acción no hay, ni debería haber, ninguna sentencia SQL. Ni siquiera del tipo findBy (métodos de la clase par)... Todas las llamadas a BD deberían estar dentro de servicios, porque sino estaría acoplando la lógica de presentación (execute del action) con la lógica de negocio (service) Y ESO NO SE HACE!:D


jueves, 15 de julio de 2010

Barajitas premiadas (#7) - eclipse.ini

Esta es una barajijta sencillita pero de gran utilidad! Cuando nuestro Eclipse se queda corto de memoria y queremos que tome más, pues simplemente debemos decirlo a la Java VM.

Para nuestra fortuna, Eclipse tiene un archivo donde podemos configurar diversos parámetros que éste considerará al iniciar, eclipse.ini ubicado en la raíz de la instalación, allí podemos indicar a la Java VM que tome más memoria heap o stack con las opciones -Xmx o -Xss respectivamente.

Generalmente es suficiente aumentar sólo el espacio heap, por ejemplo: -Xmx512M o -Xmx1G.

Espero escribir más adelante acerca del heap y el stack, de momento les dejo un interesante artículo como referencia.

Creando un proyecto en Symfony con PortgreSQL desde cero

A continuación les dejo una guía rápida para crear un proyecto en Symfony con una base de datos PostgreSQL desde cero, asumiendo que ya tenemos un script DDL y no haremos ningún módulo de momento.

Creando la BD...
$ sudo su - postgres #estoy iniciando sesión como postgres en el sistema
$ createuser myuser -P #puede hacerlo admin si lo desea, con contraseña mypassword
$ createdb mydb -O myuser #creando la BD con myuser como dueño
$ exit
$ psql mydb myuser -h localhost < scriptDDL.sql #creando los objetos en la BD
Creando el proyecto...
$ mkdir /var/www/myproj #creando el proyecto en el DocumentRoot de Apache
$ cd /var/www/myproj/
$ symfony generate:project myproj #inicializando proyecto
$ ln -s $RUTA_SYMFONY/data/web/sf web/ #copiando enlaces básicos de symfony
$ symfony generate:app myapp #inicializando aplicación
$ symfony configure:database "pgsql:host=localhost;dbname=mydb" myuser mypassword #configurando BD
$ symfony generate:build-schema #construyendo schema de la BD (config/doctrine/schema.yml)
$ symfony doctrine:build-model #construyendo modelo a partir del schema
$ symfony doctrine:build-forms #construyendo forms a partir del modelo
$ symfony doctrine:build-filters #construyendo filtros a partir del modelo
Podría ver un adelanto del proyecto, hasta ahora -sin módulos- en: http://localhost/myproj/web/myapp_dev.php

miércoles, 14 de julio de 2010

Barajitas premiadas (#6) - rsync

Esta barajita es especialmente útil! El comando rsync... Les comento el problema que me planteó utilizarlo, para ejemplificar mejor. Ni les comento de replicas para respaldos o hasta bases de datos en línea, lo máximo!

Yo tengo una cuenta en un servidor en la Web y tengo acceso SSH al mismo. La cuestión es que debo subir mis códigos al servidor, porque las pruebas que estoy haciendo deben correr allí.

Ahora, cada vez que necesitaba copiar mis archivos, debía hacerlo vía SCP o FTP (que no me gusta por temas de seguridad) y esto demoraba mucho, porque siempre debían copiarse todos los archivos. El ideal, sería copiar sólo los archivos que cambiaron para disminuir mis tiempos de despliegue. Allí es cuando entra rsync en acción. Simplemente especifico que deseo utilizar SSH, cuáles directorios copiar, cuáles excluir y a dónde enviarlos. A continuación un pequeño script, que luego retomaré porque le hice modificaciones, que espero les sea de utilidad:

#!/bin/bash

USER='a'
SERVER='example.com'
OPTS='--exclude=cache --exclude=log'
SOURCE_DIR='.'
REMOTE_DIR='~/www/myapp'

# Synchronizing files
rsync -r -e ssh $OPTS $SOURCE_DIR $USER@$SERVER:$REMOTE_DIR

martes, 13 de julio de 2010

Barajitas premiadas (#5) - Syntax Highlighter

Como podrá darse cuenta mis nuevos artículos incluyen un resaltador de sintaxis (syntax highlighter).

Los resaltadores de sintaxis mejoran la presentación de nuestros artículos. No los había utilizado hasta el momento porque no conseguía uno que realmente me gustara, hasta que por fin... El que he escogido, SyntaxHighlighter, es bastante bueno y reconoce un montón de lenguajes... Se los recomiendo!

Barajitas premiadas (#4) - Debian Backports

Barajitas premiadas (#4) - Debian Backports

Al utilizar alguna versión de Debian es común mantener paquetes y repositorios estables. Debian trabaja con diferentes repositorios, según versión, con diferentes niveles de madurez: unstable, testing, stable u oldstable.

El problema surge cuando desea utilizar paquetes más actualizados y entonces comienza a mezclar entornos estables (stable) con pruebas (testing), o hasta en casos extremos, inestables (unstable). Para estos casos, siempre que sea posible, se recomienda utilizar los repos backport.

La idea es agregar el repo a su archivo de fuentes APT y luego especificarlo cuando desee utilizarlo; esto es excelente, APT no lo considerará al buscar paquetes al menos de que usted lo indique.

Esto me fue de especial ayuda al instalar mi nueva impresora HP (Deskjet F2420). Pues resulta que el paquete hpijs-ppd estable de mi Debian Lenny no contenia el driver porque era muy reciente, entonces, configure el repo backport para Lenny y listo!!! A continuación los pasos que seguí:

# echo 'deb http://www.backports.org/debian lenny-backports main contrib non-free' >> /etc/apt/sources.list
# apt-get update
# aptitude -t lenny-backports install hpijs-ppds


Cómo incluir archivos JS dentro de otros

Cuando trabajamos con JavaScript es buena práctica colocar nuestro código dentro de archivos JS, en aras de hacer nuestro código más fácil de mantener. Luego, una vez me surgió la siguiente pregunta: considerando que tenía un JS y que quería utilizar funciones y/u objetos definidos en otro JS ¿cómo podía incluir uno en otro? Algo como los include de C o PHP.

La respuesta es: no es necesario! Todos los JS que se cargan en una página son montados en la RAM de la máquina por lo que no es necesario incluir archivos JS dentro de otros. Según lo comentado, tomando el código de nuestro artículo anterior, el ejemplo mostrado a continuación es completamente válido:

//archivo: usuario.class.js
const ROL_INVITADO = 'invitado';
const ROL_ADMIN = 'admin';

function Usuario(nombre, clave, rol) {
 var nombre = nombre;
 var clave = clave;
 var rol = rol;

 //setters
 this.setNombre = function(_nombre) { nombre = _nombre; };
 this.setClave = function(_clave) { clave = _clave; };
 this.setRol = function(_rol) { rol = _rol; };

 //getters
 this.getNombre = function() { return nombre; };
 this.getClave = function() { return clave; };
 this.getRol = function() { return rol; };

 //funciones
 this.toString = function() {
  retorno = 'nombre: ' + this.getNombre() + ', ';
  retorno += 'clave: ' + this.getClave() + ', ';
  retorno += 'rol: ' + this.getRol();
   
  return retorno;

 };
}

//archivo: cargar_usuarios.js
var usuario1 = new Usuario('rodolfo', '123456', ROL_ADMIN);
var usuario2 = new Usuario('pepe', '654321', ROL_INVITADO);

<html>
 <head>
  <title>Ejemplo clases JavaScript</title>
  <script type="text/javascript" src="usuario.class.js"></script>
  <script type="text/javascript" src="cargar_usuarios.js"></script>
 </head>
 <body>
  <script language="javascript">
   document.writeln(usuario1);
   document.writeln('<br/>');
   document.writeln(usuario2);
  </script>
 </body>

</html>

Fíjese como dentro del archivo cargar_usuarios.js no incluí el usuario.class.js. Además, dentro del html utilizo las variables declaradas en el cargar_usuarios.js sin ningún problema. Recuerde, todo queda en la RAM!


lunes, 12 de julio de 2010

Cómo trabajar OO en JavaScript

A continuación un ejemplo que permite ver cómo trabajar con "clases" en JavaScript (JS). Podrá observar que el constructor de la "clase", y lo coloco entre comillas porque más bien parece una estructura de datos potenciada, está definido por la firma de la función. Los atributos o funciones definidos con las palabras reservadas this o var son de acceso público o privado respectivamente. NOTA: Los parámetros de las funciones setters llevan el guión bajo "_", porque no podría utilizarse la variable this ya que tendrían que haberse declarado los atributos como públicos (utilizando this) y no privados (utilizando var).

Ahora el código:

<html>
<head>
<title>Ejemplo clases JavaScript</title>
<script language="javascript">
const ROL_INVITADO = 'invitado';
const ROL_ADMIN = 'admin';

function Usuario(nombre, clave, rol) {
var nombre = nombre;
var clave = clave;
var rol = rol;

//setters
this.setNombre = function(_nombre) { nombre = _nombre; };
this.setClave = function(_clave) { clave = _clave; };
this.setRol = function(_rol) { rol = _rol; };

//getters
this.getNombre = function() { return nombre; };
this.getClave = function() { return clave; };
this.getRol = function() { return rol; };

//funciones
this.toString = function() {
retorno = 'nombre: ' + this.getNombre() + ', ';
retorno += 'clave: ' + this.getClave() + ', ';
retorno += 'rol: ' + this.getRol();

return retorno;

};

}
</script>
</head>
<body>
<script language="javascript">
var usuario = new Usuario('rodolfo', '123456', ROL_ADMIN);
document.writeln(usuario);
document.writeln('<br/>');

usuario.setRol(ROL_INVITADO);
document.writeln(usuario);
</script>
</body>
</html>

domingo, 11 de julio de 2010

Barajitas premiadas (#3) - /dev/null

Esta es una barajita que voy a escribir antes de que se me olvide...

Puede parecer trivial, perooooo... El archivo null dentro de la carpeta /dev, el /dev/null, es una especie de hoyo negro de los sistemas *nix.

Por ejemplo, si queremos vaciar un archivo rápidamente...


$ cat /dev/null > NOMBRE_ARCHIVO


O, por ejemplo, si tenemos un comando que arrojará errores y no deseo hacer nada con ellos, ni siquiera echarlos a un archivo, siempre puedo enviarlos al "hoyo negro":


$ find / -name \*.conf 2> /dev/null


El comando de arriba buscará dentro de todo el sistema (desde la raíz "/") archivos que terminen con la extensión .conf. Esto retornará errores por permisos insuficientes para buscar en varios directorios (fíjese que el comando es ejecutado como un usuario ordinario del sistema, por el símbolo de "$" antes del comando y no de "#" que identifica al usuario root). Entonces estos errores (2>) son redireccionados al "hoyo negro".

Esto también es súper útil cuando configuro tareas (Ej. CRON), porque si ocurren errores, serán enviados a la bandeja de entrada de correos locales del usuario dueño de la tarea y podríamos ocupar espacio en disco innecesario. OJO, esto depende del caso y no siempre se recomienda obviar los mensajes de error.

Barajitas premiadas (#2) - man

La barajita del día... El comando man de *nix nos permite observar el manual correspondiente para una entrada (generalmente un comando). Me puedo mover con las flechas (arriba y abajo), pg down (barra espaciadora), pg up (b) o inclusive buscar patrones (presionando barra "/", colocando el patrón y ENTER). Para salir q.

Ahora, hay veces en las que tengo más de un manual para la misma entrada o simplemente deseo conocer si existe manual para algo. Para ello, puedo utilizar la opción -k del comando. A continuación presento un ejemplo que permite obtener las coincidencias para manuales de printf, obteniendo dos respuestas concretas y luego abriendo una de ellas específicamente. Fíjese que filtro la salida con el comando grep para especificar sólo las líneas que contengan la palabra print al inicio; para comprobar el comportamiento, puede probar quitando el pipe (|) y luego colocándolo de nuevo


rodolfo@rcampos-laptop:~$ man -k printf\* | grep ^print
printf (3) - conversión de salida formateada
print (1) - execute programs via entries in the mailcap file
print_description (3snmp) - mib_api functions
print_mib (3snmp) - mib_api functions
print_objid (3snmp) - mib_api functions
print_value (3snmp) - mib_api functions
print_variable (3snmp) - mib_api functions
printafm (1) - Print the metrics from a Postscript font in AFM format...
printenv (1) - print all or part of environment
printers.conf (5) - printer configuration file for cups
printf (1) - format and print data
rodolfo@rcampos-laptop:~$ man 3 printf

Cómo configurar un Trust Store en Java para conexiones sobre SSL

A continuación se presentan un conjunto de pasos que puede seguir para configurar un almacén de claves (keystore) como de "confianza" para una máquina virtual de Java. Esto resulta indispensable cuando deseamos que nuestra aplicación se conecte con servicios sobre SSL, por ejemplo: Servicios Web (sobre SSL - HTTPS), Servidores FTP (sobre SSL - FTPS) o Servidores SMTP (sobre SSL - SMTPS).

La Java VM no le permitirá conectarse con estos servicios sobre SSL a menos de que hayan sido configurados como confiables, para lo que deberá configurar el almacén de confianza (Trust Store).

  1. Obtener el certificado, para lo cual podrá correr el shell script retrieve_certificate.sh (mostrado al final de este artículo)
    $ ./retrieve_certificate.sh IP PUERTO > NOMBRE_CERT
  2. Crear el almacén de claves (keystore)
    $ keytool -import -keystore NOMBRE_KEYSTORE -file NOMBRE_CERT -alias NOMBRE_REF_CERT
  3. Chequear keystore
    $ keytool -list -keystore NOMBRE_KEYSTORE
  4. Agregar parámetros del keystore al Java VM. Por ejemplo, en tomcat:
    JAVA_OPTS="$JAVA_OPTS -Djavax.net.ssl.trustStore=NOMBRE_KEYSTORE -Djavax.net.ssl.trustStorePassword=CLAVE_KEYSTORE

A continuación el shell script retrieve_certificate.sh (extraído de aquí):

#!/bin/sh
#
# usage: retrieve-cert.sh remote.host.name [port]
#
REMHOST=$1
REMPORT=${2:-443}

echo |\
openssl s_client -connect ${REMHOST}:${REMPORT} 2>&1 |\
#openssl x509 -connect ${REMHOST}:${REMPORT} 2>&1 |\
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'

Cómo realizar queries con HQL (una sugerencia para evitar SQL injection)

Cuando trabajamos con Hibernate y deseamos realizar queries con el lenguaje de consultas propio, HQL, recomiendo trabajar con parámetros a la forma de sentencias preparadas (prepared statements). Por ejemplo:


@Override
public Usuario login(Cuenta cuenta) {

Query query = this.getEntityManager().createQuery("from Usuario where cuenta.login = :login and cuenta.password = :password ");

query.setParameter("login", cuenta.getLogin());
query.setParameter("password", cuenta.getPassword());


List result = query.getResultList();

if (result.size()==0){
return null;
}
return result.get(0);

}


En el código mostrado arriba se puede ver como los parámetros "login" y "password" son pasados en diferido y no concatenados directamente en el string, esto puede salvarle la vida contra ataques de SQL injection. No estoy seguro de que además obtenga ganancias en tiempo de ejecución, para llamadas posteriores, porque estas sentencias sean preparadas y almacenadas en el catálogo del RDBMS.

sábado, 10 de julio de 2010

Barajitas premiadas (#1) - find

Este es el primero de un seriado de artículos que estaré publicando con herramientas que siempre debemos tener a la mano. Esas herramientas que siempre nos pueden salvar la vida, barajitas! (figuras, de esas que se pegan en un álbum). Y como éstas son especialmente útiles, las he bautizado como barajitas premiadas.

La primera barajita, el siempre bien ponderado y siempre entrañable en otros sistemas no *nix, el find.

El comando find, en su forma más básica, permite buscar archivos que cumplan con algún patrón, por ejemplo, para buscar todos los archivos con extensión .php dentro del directorio /var/www (generalmente, DocumentRoot del Apache):


$ find /var/www -name \*.php


Ahora, la utilidad específica que les quiero presentar es la siguiente:


$ find DIRECTORIO_DESEADO -name ALGÚN_PATRÓN -exec COMANDO '{}' ';'


Este comando buscará dentro de un DIRECTORIO_DESEADO todos los archivos que cumplan con ALGÚN_PATRÓN y para cada uno de ellos aplicará el COMANDO deseado. Note que la cadena '{}' será reemplazada por el nombre de cada archivo que cumpla con el patrón y la cadena ';' indica el fin del comando. Veamos un ejemplo concreto y muy, muy útil:


rodolfo@rcampos-laptop:/opt/symfony-1.4.6$ find . -name \*.php -exec grep 'function form_tag(' '{}' ';'
function form_tag($url_for_options = '', $options = array())
rodolfo@rcampos-laptop:/opt/symfony-1.4.6$ find . -name \*.php -exec grep -l 'function form_tag(' '{}' ';'
./lib/helper/UrlHelper.php
rodolfo@rcampos-laptop:/opt/symfony-1.4.6$


El ejemplo de arriba muestra cómo puedo buscar dentro del directorio raíz de Symfony dónde puede estar declarada la función form_tag y luego en la siguiente línea, con la opción -l del comando grep, pido el nombre del archivo donde se haya encontrado la coincidencia. Después de esto, sólo queda abrir el archivo y ver el detalle de la función. No hace falta ni ahondar en lo poderoso y útil que puede ser esto, sobre todo al trabajar con frameworks de código abierto como Symfony. Recuerde que el comando grep busca coincidencias, según un patrón, dentro de un archivo.

Otra súper útil, cuando trabajo con Subversion (SVN) y deseo mover la carpeta de un proyecto sincronizado... Si no quiero que esta carpeta lleve consigo todos los archivos de SVN, debería borrar las carpetas .svn dentro de cada directorio. Además, esto hace que el proyecto pese mucho más de lo que debería. La barajita:


rodolfo@rcampos-laptop:/var/www/myproj$ find . -name .svn -exec rm -rf '{}' ';'

viernes, 9 de julio de 2010

Palabras necias

El blog de mi pana Edwin... http://edwinvaldez.com/

Cómo reasignar (resetear) la clave de un usuario en linux (una de tantas formas)

Esto es muy útil para admins! Este método es generalmente utilizado cuando se olvida (o alquien nos ha cambiado) una contraseña. Ahora, si realmente deseamos proteger nuestro sistema contra alquien que pueda cambiar una contraseña, teniendo acceso remoto o local al mismo, mejor investigar también otros métodos, algunos de ellos: stack overflows, exploits y symbolic links escalation.

Dicho esto, sigamos adelante, primero debe tener acceso físico a la máquina y poderla reiniciar... Si es así...

Reinicie la máquina, cuando consiga el gestor de arranque (probablemente GRUB), presione la letra e (edit), generalmente sobre la primera línea - correspondiente al kernel más actualizado.

Una vez ubicado en el modo de edición, debe colocarse sobre la línea que se refiere a la carga del kernel, debería ser la primera línea de unas 2 o 3 opciones, y cuando esté allí presione c (de command) o e (de edit) dependiendo del gestor, al presionar esto, debería ver una consola con la línea que escogió.

Al final de la linea antes de la opción ro, si es que la tiene, agregue init=/bin/sh

Luego presione enter y posicionándose nuevamente sobre la línea, presione la b (de boot) para que la máquina arranque con esa opción... Al arrancar debería caer en una consola; apenas terminado el reconocimiento de dispositivos.

Estando en la consola deberá remontar el disco duro con la opción de escritura para poder cambiar o borrar la clave del root o cualquier otro usuario, para ello deberá escribir:


# mount / -o remount, rw


Listo! Ahora sólo deberá ingresar una nueva contraseña para el usuario que desee:


# passwd CUALQUIER_USUARIO


Otra opción, en lugar de utilizar el comando passwd, es ingresar al archivo /etc/shadow y borrar el cifrado de la contraseña para un usuario, es decir, el montón de caracteres después de los dos puntos (:) seguidos al nombre del usuario, hasta los próximos dos puntos (:). El usuario quedaría sin contraseña.

Luego de hacer esto salve, reinicie y listo!

NOTA: Ubuntu instala un modo de mantenimiento que le permitirá hacer esto simplemente seleccionando la opción del gestor de arranque... Sin todo lo de init=/bin/sh y remontar el disco. Y bueno, esto siempre puede evitarse colocando contraseñas al gestor de arranque, para lo que alquien podría entrar con un live CD (montar la unidad y acceder al /etc/shadow), lo que también puede evitarse colocando inicio por disco y definiendo contraseña al BIOS, para lo que también alquien podría brincar el BIOS quitando la pila al computador (OJO. Hay programas capaces de hacer esto)... En fin, si desea evitar que alquien pueda cambiar su contraseña de esta forma, siempre puede encerrar el equipo bajo llave ;D

jueves, 8 de julio de 2010

Shell script de respaldos

Este script genera respaldos de repositorios SVN, trac y BDs PostgreSQL y las envia a un almacén secundario vía SSH. Podría utilizarse para generar respaldos en un servidor local y enviarlos a otro en la red. Se asume que para las conexiones SSH (utilizando SCP) existe relación de confianza entre los servidores (ver artículo anterior por cualquier duda).


#!/bin/sh
FECHA=`date +%d%m%y`_`date +%s`
DIRECTORIO="/respaldos"
USUARIO="a"
SERVIDOR="192.168.1.104"
USUARIO_BD="admin"

###### RESPALDO SVN ######

#Creando respaldo (comprimido) en formato dump del repo (Almacén Local)
svnadmin dump /repositorio/ | gzip > $DIRECTORIO/repo/repositorio_$FECHA.dmp.gz

#Copiando al Almacén de Respaldos
scp $DIRECTORIO/repo/repositorio_$FECHA.dmp.gz $USUARIO@$SERVIDOR:~/backup_servidor/repo/repositorio_$FECHA.dmp.gz

###### RESPALDO TRAC ######

#Creando respaldo del trac
trac-admin /trac hotcopy $DIRECTORIO/trac/trac
tar cfz $DIRECTORIO/trac/trac_$FECHA.tar.gz $DIRECTORIO/trac/trac
rm -r $DIRECTORIO/trac/trac

#Copiando al Almacén de Respaldos
scp $DIRECTORIO/trac/trac_$FECHA.tar.gz $USUARIO@$SERVIDOR:~/backup_servidor/trac/trac_$FECHA.tar.gz

###### RESPALDO BD ######

#Creando respaldo de la BD "bd_a"
pg_dump -U $USUARIO_BD bd_a | gzip > $DIRECTORIO/bd/bd_a_$FECHA.sql.gz

#Creando respaldo de la BD "bd_b"
pg_dump -U $USUARIO_BD bd_b | gzip > $DIRECTORIO/bd/bd_b_$FECHA.sql.gz

#Llevando respaldo al servidor Web de Softclear
scp $DIRECTORIO/bd/*_$FECHA.sql.gz $USUARIO@$SERVIDOR:~/backup_servidor/bd/

Cómo configurar SSH para realizar conexiones sin contraseña

Ahora sí, al grano...

Utilizar SSH para conectarse a máquinas de forma remota es muy común. Luego, hay momentos en los que desearíamos obviar el ingreso de contraseña, bien sea porque no deseamos divulgarla o simplemente porque nos fastidia tener que colocarla constantemente.

Para que una máquina pueda conectarse a otra vía SSH, sin la necesidad de colocar una contraseña, se asume que ambas mantienen una relación de confianza, en este caso unidireccional, es decir, que "A" confíe en "B", no implica necesariamente que "B" confíe en "A".

Ya en el artículo anterior, Cómo funciona SSH, explicamos algo de SSH, podría considerar este artículo como su continuación...

Supongamos que "A" desea conectarse a "B" vía SSH sin necesidad de ingresar su contraseña, o, y realmente prefiero esta explicación, "B" considera que "A" mantiene una relación de confianza con él, por lo que le permitirá iniciar una sesión para un usuario específico sin la necesidad de autenticarse explicitamente.

Lo que debe hacer "A" es generar un par de claves (pública y privada) y enviarle su clave pública a "B", para que esta la almacene en su base de datos de máquinas confiables. Luego, cuando "A" inicie sesión -para el usuario que hayan acordado- "B" le concederá el permiso sin la petición explícita de una contraseña.

Pongámonos técnicos...

Para generar la clave:


rodolfo@rcampos-laptop:~$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/rodolfo/.ssh/id_rsa): ENTER
Enter passphrase (empty for no passphrase): ENTER
Enter same passphrase again: ENTER
Your identification has been saved in /home/rodolfo/.ssh/id_rsa.
Your public key has been saved in /home/rodolfo/.ssh/id_rsa.pub.
The key fingerprint is:
9d:0f:4e:8c:b5:bb:f4:c3:ec:e3:79:8b:59:0b:1a:d1 rodolfo@rcampos-laptop
The key's randomart image is:
+--[ RSA 2048]----+
| |
| |
| . |
| = + |
| S O E |
| o = |
| =oo . |
| . ==*.. |
| ooB+o. |
+-----------------+
rodolfo@rcampos-laptop:~$


Ahora tenemos la clave pública en la carpeta .ssh ubicada en nuestro $HOME (/home/rodolfo/.ssh/id_rsa.pub), sólo nos resta enviarla al host remoto y que en este se agregue al archivo authorized_keys dentro del directorio .ssh ubicado en el $HOME del usuario seleccionado. Por ejemplo:


rodolfo@rcampos-laptop:~$ scp /home/rodolfo/.ssh/id_rsa.pub a@myhost.com:~/.ssh
a@myhost.com's password:
id_rsa.pub 100% 404 0.4KB/s 00:00
rodolfo@rcampos-laptop:~$ ssh a@myhost.com "cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys"
a@myhost.com's password:
rodolfo@rcampos-laptop:~$ ssh a@myhost.com
Last login: Mon Jul 5 06:45:40 2010
a@myhost.com [~]#

Cómo funciona SSH

Realmente quería hablar de conexiones sobre SSH confiables o sin contraseñas, pero primero debemos entender SSH, hagámoslo con un ejemplo:

"A" necesita comunicarse con "B", pero no podría hacerlo directamente porque otro nodo, como "C", podría interceptar la comunicación y enterarse de cosas que no debería. Por ello, "A" y "B" deben comunicarse bajo un canal seguro. Este canal seguro es establecido bajo un canal cifrado haciendo uso de una clave "que sólo ellos dos conocen"; específicamente son utilizados algoritmos de cifrado simétrico para realizar esto.

Ahora, el único problema a este punto es la clave que "A" y "B" deberían compartir (secreto compartido). Para enterarse cada uno de ésta, antes la misma debe ser generada (por uno de ellos) y compartida (enviada al otro).

La clave (a compartir) es enviada al otro bajo un canal seguro empleando algoritmos de cifrado asimétrico; por supuesto, también la clave es compartida sobre un canal seguro!!

La comunicación sobre canales cifrados con algoritmos asimétricos necesita de dos claves por cada nodo. Es decir, "A" y "B" tendrían dos claves cada uno.

La idea es que cada uno de los nodos tenga una clave privada (que sólo él y más nadie conoce) y una pública (que como el nombre lo dice, todos conocen o al menos podrían). Luego, cuando los mensajes son cifrados con la clave privada, sólo pueden descifrarse con la pública (o viceversa).

Ahora, por fin al grano, "A" desea comunicarse con "B", entonces: "A" genera una clave, la cifra con la clave pública de "B" y envía el resultado (mensaje) a "B". Luego, "B" descifra el mensaje (la clave compartida) con su clave privada y voalá!, a este punto "A" y "B" tienen un secreto compartido. De aquí en adelante, toda la comunicación es cifrada con un algoritmo simétrico.

Podrían preguntarse ¿para qué todo este problemón?, ¿por qué no realizar toda la comunicación sobre canales cifrados con algoritmos asimétricos? La respuesta es que se podría, pero sería demasiado lento!! Por ello, generalmente los algoritmos de cifrado asimétrico son utilizados únicamente para compartir claves que después servirán para cifrar canales con algoritmos simétricos.

Increible todo el tema de cifrado, eh? Bruce Schneier en su Applied Cryptography, literatura obligada para cualquier profesional del área de seguridad, escribió: "The lesson here is that it is insufficient to protect ourselves with laws; we need to protect ourselves with mathematics..."

miércoles, 7 de julio de 2010

Cómo utilizar Git sobre Hostmonster o Bluehost

Git es un Sistema para Controlar Versiones (SCV) inventado por Linus Torvalds. Fue diseñado considerando el proceso de desarrollo de Linux y hoy día es uno de los SCV más utilizados.

Lo que hace a Git tan atractivo es la capacidad de manejar versiones de forma distribuida bajo un enfoque KISS (Keep it simple, stupid!). Por ejemplo, Git es perfecto para un esquema típico de desarrollo de software libre, donde se crean unidades de desarrollo con líderes que deben velar porque todo lo desarrollado por sus supervisados esté de la mejor forma posible. Luego, estos líderes son los encargados de subir sus códigos a repositorios de datos con mayor visibilidad, para que sus líderes validen el trabajo y así sucesivamente.

Además de lo mencionado anteriormente, Git brinda un manejo excepcional de branches y tags!!

El objetivo de este artículo es explicar cómo pude trabajar con un repositorio Git en Hostmonster (hostmonster.com).

Primero: Instalando Git

Debe instalar Git en su máquina para poderlo utilizar, hacerlo, es muy sencillo:

# aptitude install git-core

Luego, lo que puede resultar un tanto problemático es instalar Git en Hostmonster donde por supuesto no tenemos permiso para instalar paquetes. Entonces la idea es descargar los fuentes de la aplicación, compilarlos e incluir la ruta bin del compilado en la variable de entorno PATH. En el siguiente enlace puede conseguir las instrucciones detalladas de cómo hacer esto. Lo único que cambié, fue el paquete Git, descargué la última versión estable (para la fecha de escritura de este artículo 1.7.0).

Segundo: Configurando el repositorio

Debe conectarse a su servidor de Hostmonster (para lo cual debe tener acceso SSH), crear la carpeta donde almacenará la información e iniciar el repo Git crudo (desnudo - bare). Para efectos de este ejemplo llamaré a mi sitio git-example.com.


$ ssh git@git-example.com
$ mkdir -p repo/myproj.git
$ cd repo/myproj.git
$ git --bare init


Luego, en su máquina deberá crear la carpeta donde ubicará los fuentes del proyecto, iniciar el repo Git, agregar al menos un archivo (para generar el branch master), configurar las variables uploadpack y receivepack (que el Git remoto utilizará para hacer push y pull), configurar el origen remoto para su repo y realizar el primer push.

Si a este punto tiene alguna duda acerca del funcionamiento de Git, de las operaciones push y pull le aconsejo leer del siguiente enlace.


$ mkdir -p projects/myproj
$ cd projects/myproj
$ git init
$ echo "README" > README
$ git add README
$ git commit -a -m "Initial commit"
$ git config remote.origin.uploadpack '$RUTA_REMOTA_ABSOLUTA_GIT/bin/git-upload-pack'
$ git config remote.origin.receivepack '$RUTA_REMOTA_ABSOLUTA_GIT/bin/git-receive-pack'
$ git remote add origin ssh://git@git-example.com/home/git/repo/myproj.git
$ git push origin master


De aquí en adelante trabaje tranquilamente y cada vez que desee hacer un commit repita los pasos:


$ git add *
$ git commit -a -m "Commit description"
$ git push


El resto de los usuarios podrá clonar el repo de la siguiente forma:


$ git clone -u '$RUTA_REMOTA_ABSOLUTA_GIT/bin/git-upload-pack' ssh://git@git-example.com/home/git/repo/myproj.git

martes, 6 de julio de 2010

Cómo interconectar PostgreSQL con Oracle

Este artículo explica cómo obtener datos almacenados en una BD Oracle desde PostgreSQL (a través de una vista). Esto permite obtener datos en línea y sin la necesidad de replicar la información en diferentes BDs.

La idea es crear una vista, que invocará un procedimiento almacenado, que se conectará a la BD Oracle y extraerá los datos.

El procedimiento almacenado fue programado en Java y registrado en PostgreSQL utilizando la librería Pl/Java.

Notas:

Este ejemplo fue montado sobre una máquina con Linux Debian Lenny. Para la versión actual de Pl/Java se tuvo que descargar el jdk 1.4 más reciente (aún y cuando ya fue declarado como obsoleto) debido a que la librería fue desarrollada con esta versión.

Es importante destacar que el procedimiento almacenado desarrollado fue compilado con la versión 1.4 del jdk.

Primero: Instalación de Pl/Java

Lo primero que debemos hacer es generar los archivos pljava.so y pljava.jar para que utilicen la versión de Sun del jdk y no la libre GCJ (esto en caso de que no se desee utilizar esta versión).

En el siguiente vínculo puede conseguir una guía que explica bastante bien cómo generar estos archivos (pljava.so y pljava.jar). Luego, puede descargarlos directamente de aquí. Recuerde que estos archivos fueron generados con la versión 1.4 del jdk de Sun.

En caso de que decida generar estos archivos usted mismo, es importante destacar que necesitará instalar los paquetes: libpq-dev y postgresql-server-dev-8.3.

Una vez descargados (o generados) los archivos, deberá copiarlos a la ruta donde PostgreSQL busca las librerías, por ejemplo: /usr/lib/postgresql/8.3/lib/

Ahora deberá definir las siguientes variables de entorno dentro del archivo environment de PostgreSQL (Ej. /etc/postgresql/8.3/main/environment):


JAVA_HOME = '/opt/j2sdk1.4.2_19'
LD_LIBRARY_PATH = '/opt/j2sdk1.4.2_19/jre/lib/i386:/opt/j2sdk1.4.2_19/jre/lib/i386/client'


Ambas variables de entorno permitirán a PostgreSQL localizar el jdk de Java y los archivos de librerías del sistema de Java (.so o .dll según el SO).

Una vez configurada la librería pljava y definidas las variables de entorno, se deberá correr el script install.sql incluido con la librería, para definitivamente culminar la instalación. El script podrá correrlo sobre la BD template1, para que todas las BD puedan utilizar la librería; sobre un template que usted defina, para habilitar determinadas BD; o directamente sobre la BD de su proyecto, para habilitarla sólo a ella.

Segundo: Programación del Procedimiento

En el siguiente vínculo puede descargar los fuentes de la aplicación. Podrá observar que los fuentes son acompañados de un jar que los contiene, esto debido a que los mismos deben ser empaquetados en un JAR para registrarlos posteriormente en PostgreSQL.

Importante: Deberá colocar el driver JDBC de Oracle acorde a la versión del jdk, en este caso 1.4 (ojdbc14.jar), dentro de la carpeta $JAVA_HOME/jre/lib/ext.

Tercero: Registrando el procedimiento almacenado

Luego deberá conectarse a la BD, registrar el JAR que contiene los procedimientos almacenados, definir un nuevo schema, definir un nuevo classpath, definir el procedimiento almacenado y definir la vista correspondiente. Por ejemplo:


SELECT sqlj.install_jar('file:///home/rodolfo/sigcne/sigcne.jar', 'sigcne', false);
CREATE SCHEMA oracle;
SELECT sqlj.set_classpath('oracle', 'sigcne');
CREATE FUNCTION oracle.getData() RETURNS SETOF varchar AS 'oracle.OrclConnection.getData' IMMUTABLE LANGUAGE java;
CREATE VIEW centros_votacion AS SELECT oracle.getData();


Cuarto: Invocando la vista

Sencillamente puede llamar la vista de la siguiente forma:


SELECT * FROM centros_votacion;

Cómo convertir archivos SHAPE a MySQL

Para convertir archivos SHAPE a MySQL debe utilizarse el comando shp2mysql de la siguiente forma:


$ shp2mysql -s 4326 -d shps_estado_yar/yar_poblaciones_font_point.shp yar_poblaciones_font_point camposer > mysql/yar_poblaciones_font_point.sql


Donde:
  • La opción -s específica el datum. Si no es especificado coloca -1 por omisión, luego, la mayoría de cartografía que se encuentra pública posee como datum el EPSG 4326 (WGS84).
  • El archivo .shp que desea transformar. Se coloca seguido de la opción -d
  • El nombre de la tabla
  • El nombre de la BD

Cómo convertir archivos SHAPE a PostgreSQL (PostGIS)

Para convertir archivos SHAPE a PostgreSQL (PostGIS) debe utilizarse el comando shp2pgsql de la siguiente forma:


$ shp2pgsql -s 4326 ~/Desktop/shp_yar/yar_estados_region.shp yar_estados_region > yar_estados_region.sql


Donde:
  • La opción -s específica el datum. Si no es especificado coloca -1 por omisión, luego, la mayoría de cartografía que se encuentra pública posee como datum el EPSG 4326 (WGS84).
  • El archivo .shp que desea transformar
  • El nombre del esquema, seguido de la tabla que se generará. En caso de no colocar esquema (formato esquema.tabla), el comando asume el esquema public
  • El nombre del archivo donde se desea enviar el SQL generado

Cómo crear y trabajar con procesos batch en Symfony 1.2

Aquí les dejo un ejemplo (Hola mundo!):


rodolfo@rcampos-laptop:/var/www/proy$ symfony generate:task pbatch:exec --use-database=false
>> task Creating "/var/www/proy/lib/tas...chExecTask.class.php" task file
rodolfo@rcampos-laptop:/var/www/proy$ vi lib/task/pbatchExecTask.class.php
rodolfo@rcampos-laptop:/var/www/proy$ symfony pbatch:exec
Hola mundo
rodolfo@rcampos-laptop:/var/www/proy$

Cómo exportar e importar bases de datos Oracle

A continuación se explica el método de exportación e importación utilizando sqlplus.

Primero que nada debe chequear que la variable ORACLE_HOME esté seteada y sería bueno incluir los binarios de Oracle en el PATH. Por ejemplo:


export ORACLE_HOME=/usr/lib/oracle/xe/app/oracle/product/10.2.0/server
export PATH=$PATH:/usr/lib/oracle/xe/app/oracle/product/10.2.0/server/bin


Luego, al ejecutar el sqlplus la conexión deberá ser de la forma:


$ sqlplus USUARIO/PASSWORD@IP_BD/SERVICE_NAME


Para generar el respaldo (export):


SQL> host exp USUARIO/PASSWORD owner=DUEÑO_BD file=NOMBRE_ARCHIVO.dmp


Para restaurar el respaldo (import):


SQL> host imp USUARIO/PASSWORD fromuser=DUEÑO_BD file=NOMBRE_ARCHIVO.dmp touser=USUARIO;


En caso de que deba borrarse el usuario de la base de datos antes de restaurar el respaldo (si hay tablas preexistentes). Esto debe hacerse conectado como otro usuario:


SQL> drop user USUARIO casade;
SQL> create user USUARIO identified by "PASSWORD" default tablespace TABLESPACE quota unlimited on TABLESPACE;
SQL> grant connect,resource to USUARIO;