Inicio > Tips Currículum Vitae Tutorial mod_perl

Tips

He preparado un documento con algunas ideas que me parecen importantes con respecto a los movimientos free software y opensource.

Colocar campos que automáticamente auditen las fechas de ingreso y modificación en una tabla postgresql.

En la consola psql creamos los campos.

La fecha de ingreso queda lista de inmediato con la cláusula default.

alter table xxx add fec_ing timestamp;
alter table xxx alter column fec_ing set default current_timestamp;

El asunto es la de modificación

alter table xxx add fec_mod timestamp;

En bash como usuario postgres es necesario cargar el lenguage plpgsql en la base de datos para que podamos hacer triggers usando SQL y no C.

-bash-2.05b$ createlang plpgsql nombredelabasededatos

En la consola psql creamos la función que llamaran los triggers.

CREATE FUNCTION act_fec_mod() RETURNS trigger AS '
BEGIN
NEW.fec_mod := ''now'';
RETURN NEW;
END;
' LANGUAGE plpgsql;

Ojo, esta función la podemos usar para cualquier tabla que tenga un campo que se llame fec_mod.

CREATE TRIGGER update_xxx BEFORE UPDATE ON xxx
FOR EACH ROW EXECUTE PROCEDURE act_fec_mod();

...y listo, cambien xxx por la tabla que desean auditar, y si han manejado como debe ser: campos nombrados al hacer sus insert, todo debe andar sin problemas.

Esta es una sesión haciendo todo

Ir al principio

Instalacion de Oracle 10g en Linux

Me tocó instalar en forma remota en una máquina sin CDs corriendo RedHat 9 el oracle 10g, los pasos son los siguientes. Esta pauta de instalación también fue probada en Fedora Core 1.

Bueno, lo primero es descargar el archivo con el servidor oracle desde oracle, el nombre debe ser algo así como ship.db.cpio.gz y pesa algo más que 600 megas. En mi caso lo descargue en la carpeta /backup/oracle. En la siguiente dirección deben bajar la base de datos, hay que registrarse para poder hacerlo.

http://otn.oracle.com/software/products/database/oracle10g/index.html

Lo dejan en un sistema de archivos que tenga al menos unos 700 megas libres, pues vamos a tener que descomprimr el archivo. Lo descomprimen con:
gunzip ship.db.cpio.gz
cpio -idv < ship.db.cpio

Esto crea una carpeta llamada Disk1 donde queda todo.

Vamos a hacer una instalación sin interfaz gráfica, para ello vamos a usar un archivo que en el fondo tiene toda la configuración que uno debería hacer a haciendo clicks en el asistente de instalación java.

Nos vamos a parar en la Carpeta Disk1 recién creada al descromprimir el archivo con cpio.

cd Disk1

Editar el archivo response/standard.rsp de acuerdo a lo que deseen, acá adjunto el mio. Les recomiendo usar este, y modificar las siguientes entradas:

FROM_LOCATION=/backup/oracle/Disk1/stage/products.xml
 
ORACLE_HOME=/var/ohome1
 
ORACLE_HOME_NAME=ohome1

COMPONENT_LANGUAGES={"es_ES"}

s_mountPoint=/var/oradata

s_globalDBName=pciud

s_dbSid=pciud

Debemos engañar al instalador de oracle para que crea que estamos en red hat 3 y no moleste.

echo "Red Hat Linux release 3 (Shrike)" > /etc/redhat-release

En mi caso la máquina no corre X, y no lo vamos a usar, pero el instalador necesita que esté definida la variable DISPLAY que indica el servidor X a usar, por ello se debe hacer:

export DISPLAY=localhost:0.0

Crear la carpeta donde vamos a instalar Oracle, luego deberemos crear una variable de ambiente ORACLE_HOME que apunta a ella.

mkdir /var/ohome1

Crear la carpeta donde van a quedar los archivos de base  de datos.

mkdir /var/oradata

Crear usuario y grupo.

groupadd dba
useradd -g dba oracle
chown oracle.dba /var/ohome1
chown oracle.dba /var/oradata

Debemos ejecutar el instalador como usuario oracle.

su oracle

Finalmente tiramos el instalador.

./runInstaller -silent -responseFile /backup/oracle/Disk1/response/standard.rsp

En caso de algun problema con el instalador, debe limpiarse el directorio donde estamos instalando.

rm -Rf /var/ohome1/*
rm -Rf /var/ohome1/.*

Los siguientes comandos son como root

su - root

Luego nos piden ejecutar este script que queda en el home directory del usuario oracle.

/home/oracle/oraInventory/orainstRoot.sh

Dejamos la carpeta de oracle como home directory del usuario oracle.

usermod -d /var/ohome1 oracle

Crear una sesion de usuario oracle
Ir al principio
su - oracle

Agregar lo siguiente en el .bash_profile del usuario oracle

PATH=$PATH:$HOME/bin
 
ORACLE_HOME=/var/ohome1
ORACLE_SID=pciud
 
export PATH ORACLE_HOME ORACLE_SID

Copiar el archivo que indica la instancia a etc

cp install/oratab  /etc/

Ahora ya podemos conectarnos a la consola como administrador, y subir la base de datos.

sqlplus / as sysdba
startup

y luego bajarla

sqlplus / as sysdba
shutdown immediate

Necesitamos levantar el listener para que las aplicaciones se puedan comunicar con oracle

lsnrctl start

Luego hay que crear un script de partida, este es el mio, se llama en este caso oracle. Hacer vi /etc/rc.d/init.d/oracle. y pegar lo siguiente.

#!/bin/sh
# oracle        This is the init script for starting up the Oracle
#               server
#
# chkconfig: - 85 15
# description: Starts and stops the Oracle database \
#              all database requests.
# processname: dbstart
# pidfile: /var/run/oracle.pid
;;        
ORA_HOME=/var/ohome1
ORA_OWNER=oracle

export ORACLE_HOME=/var/ohome1
;               
if [ ! -f $ORA_HOME/bin/dbstart ]
then
     echo "Oracle startup: cannot start"
     exit
fi
;               
case "$1" in
     'start')
;               
          # Start the Oracle databases:
          # The following command assumes that the oracle login
          # will not prompt the user for any values
;               
     su - $ORA_OWNER -c $ORA_HOME/bin/dbstart &
     ;;
;               
     'stop')
;               
          # Stop the Oracle databases:
          # The following command assumes that the oracle login
          # will not prompt the user for any values
;               
     su - $ORA_OWNER -c $ORA_HOME/bin/dbshut &
     ;;
;               
esac

Hacer ejecutable el script de partida y engancharlo para que comience automáticamente. Puede crear directamente los enlaces simbólicos en las carpetas /etc/rc*.d/ ... o usar chkconfig:

chmod 755 /etc/rc.d/init.d/oracle
chkconfig --add oracle

Como root levantamos la base de datos

su -
/etc/rc.d/init.d/oracle start

Ir al principio

Bloquear MSN

Hace poco me pidieron bloquear el MSN a algunas personas de la organización, este script recibe las ips que deseas bloquear como parámetro en la línea de comandos. Aparecen en duro los rangos de direccioners de M$, pero funciona impecable.

#!/bin/sh
   
EXITIF=eth0
   
for ip in $*
do
        echo "iptables -I OUTPUT -s $ip -o $EXITIF -d 207.46.96.0/19 -j DROP"
        echo "iptables -I FORWARD -s $ip -o $EXITIF -d 207.46.96.0/19 -j DROP"
done

En mi caso lo llame /usr/local/bin/closemsn, entonces la llamada es:

closemsn 10.0.0.155 10.0.0.112 10.0.0.95 10.0.0.1 10.0.0.221 10.0.0.254 10.0.0.203

Ir al principio

Convertir un string con varias palabras (un nombre) en formato mayúsculas en la primera letra y minúsculas en las otras.

Hacer una función Perl para realizar el trabajo, debe manejar correctamente la internacionalización, en nuestro caso las eñes, y los acentos, por ello usamos el pragma "use locale".

sub fixcase {
use locale;
return join " ", map {s/^(\w)(\w+)/\U$1\L$2/; $_} split /\s+/, shift;
}

Luego la llamada es:

$s = "HÉCTOR ACUÑA";
print &fixcase($s);

Esto imprime: Héctor Acuña

La primera extensión de esta aplicación es cambiarla para que deje en minúsculas las preposiciones, conectivos, y otros elementos gramaticales que normalmente no van en mayúsculas, ej: a, y, el, etc.

Ir al principio

Ir al principio

Eliminar los saltos de linea y comentarios que encontramos en archivos de configuración.

Hay archivos de configuración que traen (y está bien que así sea) grandes trozos de comentarios y saltos de línea, que luego de un tiempo hacen engorrosa la revisión de los mismos, la siguiente línea usa grep y perl para eliminarlos, lo pueden probar con el archivo de configuración de apache.

grep -v ^# httpd.conf | perl -ne '$file .= $_; END {$file=~s/\n{2,4}/\n/g; print $file}'

Esta otra versión es ligeramente diferente y sólo usa perl:

perl -ne '$file .= $_ unless /^\s*#/; END {$file=~s/\n{2,4}/\n/g; print $file}' /prod/conf/httpd.conf

Ir al principio

Persiguiendo Constraints de tablas en Oracle

Como saben, Oracle no se ha preocupado mucho de mejorar su viejo acceso de consola sqlplus, al lado de los excelentes psql o mysql se ve más patético aun. La primera recomendación es al menos instalar gqlplus que es un super wrapper sobre sqlplus y lo deja con historial y un buen ambiente de edición. Bueno vamos al tip: Estoy reparando unas aplicaciones y se cae un borrado en cascada, lo más sano y primero es ver que constraints hay en la tabla y como se llaman, en este caso las constraints y las columnas de la tabla usr.

SQL> select column_name, position, constraint_name
from User_cons_columns
where  constraint_name in (select constraint_name from user_constraints where table_name = 'USR');

La verdad es que luego por nombre borre una con:

SQL> alter table usr drop constraint SYS_C005916;

Y finalmente redefiní la llave foranea que me molestaba:

SQL> alter table usr add constraint osc_usr foreign key (id_osc) references osc(id_osc) on delete cascade;

Ir al principio

Cambiar la extensión a un grupo de archivos

Obtenido de la siguiente lista de discusión http://www.redhat.com/archives/redhat-list/2002-February/msg00977.html

El truco es usar el comando basename con la opción de eliminar el texto que uno quiere al final del string entregado (no necesariamente un archivo, como siempre la filosofía de crear herramientas potentes). Para ver bien el efecto se puede probar:

#basename miarchivo.txt
miarchivo.txt
#basename miarchivo.txt .txt
miarchivo

Luego para hacerlo en un conjunto de archivos reemplazando la extensión original por otra:

for file in *.oldext; do newname=`basename "$file" .oldext`.newext; mv "$file" "$newname"; done

  1. Usamos el comando interno de bash for para ejecutar otros comandos sobre cada uno de los elementos de la lista.
  2. Luego formamos la variable newname que contendrá el nuevo nombre del archivo, el cual creamos usando basename. Acá se usa comillas al revés backquotes `` que ejecutan el comando y retornan lo enviado en la salida estandar por el comando ejecutado sin el salto de línea final. Esto hace que se le asigne efectivamente el nombre creado a la variable $newname.
  3. Cambiamos el nombre usando mv.

Bueno, como debe ser, luego de usarlo un par de veces hice el shell script, el cual deben colocar en /usr/local/bin. Si le colocaron por ejemplo cambiaext, se usa así:
#cambiaext .jpg .gif GU*.jpg

Acá va el código del script cambiaext.

#!/bin/sh
 
oldext=$1
newext=$2
shift
shift
 
#
# Las extensiones incluyen el punto, da legibilidad y para poder eliminar las extensiones al reemplazar por vacio
# el segundo parámetro.
#
 
for file in $@
do
        newname=`basename "$file" $oldext`$newext;
        mv "$file" "$newname"
done

Ir al principio

Crear servidores de http para probar los requerimientos que hacen los clientes web en Perl.

Esta necesidad me surgió cuando tuve que hacer unos clientes usando XMLRPC en Perl, lo directo era usar el módulo XMLRCP::Lite, pero no estaba tan facil. Las personas no tenían las especificación de la petición, y yo tampoco había usado este protocolo (bastante aparatoso y no opensource en todo caso), me pasaron un programa cliente PHP que hacía la peticion correctamente. Dentro de las alernativas la más clara fue instalar PHP y correr el cliente que me habían enviado contra mi servidor, el cual escribe todo lo que le envían en la salida estandar, con lo cual pude examinar la petición con la cual pude depurer el cliente Perl.

Código original de Servidor perl obtenido del artículo: Strictly On-Line: Network Programming with Perl, el original ha sido recortado para mostrar programación rápida en Perl.

#!/usr/bin/perl -w
              
use IO::Socket;
my $sock = new IO::Socket::INET( LocalHost => 'localhost', LocalPort => 80, Proto     => 'tcp',
                   Listen    => SOMAXCONN,
                   Reuse     => 1) || die "problemas al crear socket: $!";

while ($new_sock = $sock->accept()) {
    while (defined ($buf = <$new_sock>)) {
        print $buf;
    }
}

Este es un cliente HTTP

#!/usr/bin/perl -w

use IO::Socket::INET;
              
my $sock = IO::Socket::INET->new("www.yahoo.com:80") or die $@;
print $sock "GET / HTTP/1.0\n\n";
while (<$sock>) {
    print;
}

Ir al principio

Implementar Cuotas en Linux

Tengo un servidor con tres mil usuarios activos, lamentablemente no instalé cuotas desde un principio y se me está acabando el espacio en disco, pero con Linux siempre se puede.

El control de cuotas es por cada filesystem

Primero que nada, lo ideal (que ya no hice) es tener en un śolo filesystem todos los archivos que generan los usuarios, al menos en el sistema de correo normalmente en /var/spool/mail y home. El problema es que los home directories quedan por defecto en /home y los mail box de correo quedan en /var/spool que normalmente quedan en dos filesystems distintos, por lo tanto hay que hacer dos configuraciones de cuotas que viven en mundos distintos.

En mi caso particular estoy usando el webmail openwebmail, que utiliza el home directory del usuario para almacenar todas sus bases de datos. Por otro lado el MTA que estoy usando es sendmail que finalmente via procmail coloca los correos que van llegando al mailbox de los usuarios ubicado en /var/spool.

Lo ideal es aplicar cuotas sólo en el filesystem /home y hace que /var/spool/mail sea un enlace simbólico a alguna carpeta de home.
En mi caso, por el momento sólo he colocado cuotas en /home.

Soporte en el filesystem, /etc/fstab

Cuando el kernel monta el filesystem debe habilitarle soporte para cuotas. Para ello debemos indicar en la tabla de montaje de archivos /etc/fstab tal situación. Editar /etc/fstab y cambiar la entrada del cuarto campo en el filesystem que nos interesa agregando ,usrquota,grpquota sin espacios (recordar que en este archivo el espacio (tab o espacio) son los delimitadores de campo).

Editar
vi /etc/fstab

Dejar así
LABEL=/home             /home                   ext3    defaults,usrquota,grpquota   1 2

Hay que montar y desmontar el filesystem el cual le has cambiado los indicadores en el fstab. Si lamentablemente has dejado un sólo filesystem y dado que no puedes desmontar la raiz (/) con el kernel activo, es necesario rebootear luego de esta modificación, en caso contrario hay que desmontar el filesystem y volverlo a montar, lo más directo es hacer:

umount /home y luego mount /home

También se puede hacer un mount con opción remount que es lo mismo.

mount -o remount /home

Esto a veces no es posible hacerlo pues pueden haber procesos accediendo a archivos en este filesystem.

Archivos abiertos = problemas al desmontar

Si hay archivos abiertos en un filesystem, este no se puede desmontar, y el sistema indicará filesystem ocupado. Debemos hacer que se cierren todos los archivos abiertos en el filesystem en cuestión. La idea es descubrir que procesos estan usando el filesystem, para luego terminarlos en forma normal o usando kill.

lsof | grep home
o
fuser -muv /home

fuser tiene una opción para hacer un kill automático de los procesos que tiene archivos abiertos en el filesystem. No es recomendado pero es lo más rápido. En mi caso baje manualmente los procesos que accedían al filesystem.

Una vez que no hay archivos abiertos debes tirar de nuevo el mount -o remount /home.

Creación de Archivos para Configuración de Cuotas

El sistema de cuotas coloca sus archivos de configuración en la raiz del filesystem que se está configurando.

El programa que se usa para revisar las cuotas también se usar para crear o inicializar estos archivos de configuración.

quotaoff  /home/
/sbin/quotacheck -mavug
quotaon  /home/

Este programa crea los archivos en la raiz del filesystem: aquota.user y aquota.group

Valores de las cuotas

Hay dos valores, el softlimit el cual una vez sobrepasado emite warnings y el hardlimit que no se puede pasar.

Para evitar problemas en las aplicaciones, en mi caso: el mismo programa de webmail que los usuarios necesitan usar para borrar correos no camina si se está excediendo el hardlimit. Hay que partir con un softlimit real, pero un hardlimit un par de megas mayor que el usuario que tiene más espacio usado. Para ver cuanto es lo máximo que tiene usado un usuario hay que pararse en el filesystem que nos interesa y hacer:

du /home --max-depth=1 | sort -n +0 | tail

Al final de esta lista aparecen los usuarios que estan usando más espacio en disco, se debe tomar la última entrada y colocarla como hardlimit.

Cuotas de grupo

En mi caso no las he usado pues me interesa el espacio de cada usuario, no hay archivos compartidos entre ellos. Inicialmente penśe que las cuotas de grupo tenian que ver con la idea de crear un perfil de cuota para los usuarios del grupo, y de ese modo no tener que configurar cada usuario. En realidad tiene directamente que ver con el group owner del archivo, y aplica para archivos de grupos de trabajo.

Creando el archivo de cuotas maestro

El sistema de cuotas se debe configurar para cada usuario. Normalmente se configura un usuario y luego se aplica su configuración a los otros, esto también se usa cuando se desea hacer cambios. Eventualmente en forma posterior se pueden modificar los archivos individuales para personas con permisos especiales.

edquota hans

Disk quotas for user hans (uid 4887):
  Filesystem                   blocks       soft       hard     inodes     soft     hard

  /dev/sda3  156      60000     240000         33        0        0

Lo único que cambié es que puse 60000 en soft y 240000 en hard. La verdad es que los 240 megas corresponden al usuario que más espacio estaba usando que calculamos más arriba, la idea es que el pueda trabajar y borrar los correos. Eventualmente yo podría borrar directamente los archivos pero no es correcto.

Generando los archivos de cuotas para el grupo de usuarios que me interesa

Cuando lo he modificado, tengo que hacer:
edquota -p hans `awk -F: '/mailhosting/ {print $1}' /etc/passwd`

...lo que equivale a decir # edquota -o hans juana rosa pepe julio etc

La verdad es que home directory de mis usuarios es /home/mailhosting, lo único que hago con awk es filtrar los usuarios que tienen esa carpeta como home directory. En el tip de linuxparatodos que sale más abajo generan el listado para los usuarios con userid mayor a 500, usando igualmente awk:

edquota -p hans `awk -F: '$3 > 510 {print $1}' /etc/passwd`

Revisar el estado actual de las Cuotas

Para ver el consumo actual de espacio por cada usuario se usa el comando repquota, en este caso estamos viendo ordenado por uso de espacio:
repquota /home |sort -n +5|more

Enlaces de referencia:

http://howtos.linux.com/howtos/Quota.shtml
http://www.linuxparatodos.net/linux/04-disk-quota.php
http://www.redhat.com/docs/manuals/linux/RHL-8.0-Manual/admin-primer/s1-storage-quotas.html

Sacar la consultas de postgresql en formato csv

Nada más entretenido que el shell para sacar datos de postgresql: echo "select * from .." | psql mydb... el problema es compartirlos tabulados con otros usuarios, que muchas veces usarán excel. Hasta hace un rato estaba usando psql2csv para convertir las tablas sql a formato csv, impecable trabajo, el asunto es que a veces es el resultado de una consulta sql y no precisamente una tabla lo que se necesita enviar.

Escribí un utilitario totalmente pequeño que le falta control de errores y documentación pero que funciona impecable (en realidad estoy escribiendo este tip para que no se me olvide o pierda).

La forma de uso es la siguiente:
query2csv --dbname mydabatase --sql="select * from ..." > myfile.csv

Parámetros de línea de comandos:
dbname: el nombre de la base de
sql: la query a realizar. 

He agregado los siguientes tres parámetros: host, user y pass, más ayuda:
query2csv --help

Si la consulta no es entregada en la línea de argumentos usando --sql, se lee de la entrada estandar.

Descargar query2csv

Ir al principio

Modificar un string en varios archivos

Este problema es bastante viejo, la idea central y el nombre lo dice todo, el algoritmo es:

  1. Usar el shell para iterar sobre el conjunto de archivos, para cada archivo
  2. Modificar el archivo en uno temporal usando algun filtro: sed, perl, awk
  3. Cambiar el archivo nuevo por el modificado

En mi caso le coloqué modfiles y lo deje en /usr/local/bin. La llamada es:

modfiles oldtext newtext files ...

El código usando bash con perl es el siguiente:
#!/bin/sh
 
if [ $# -le 3 ]
then
        echo "Al menos deben ser 3 parámetros"
        exit 1
fi
 
ori=$1
new=$2
shift
shift
for file in "$@"
do
        echo "modificando $file"
        tmpfile=/tmp/modif.$$
        perl -pe "s/$ori/$new/g" $file > $tmpfile
        mv -f $tmpfile $file
done
exit 0

TODO: La implementación es bastante directa del algoritmo, hay que tener cuidado en que las variables $ori y $new deben ser escapadas de caracteres especiales para llamar a perl, en particular, el slash "/" es parte de la sintaxis de reemplazo de perl. Si $ori o $new tuvieran un slash, el código recién mostrado no funcionaría. Esto aplica a otros caracteres especiales.
Es importante notar la llamada a $@ entre comillas, esto permte que bash conserve cada archivo como un argumento separado a pesar de que el nombre del archivo contenga espacios.

Ir al principio

 Borrar los correos infectados en mailboxes formato mbox con clamav

Uno de los antivirus opensource más populares es clamav. Para chequear y limpiar esta el utilitario clamscan

En Linux, cuando usas sendmail+procmail+openwebmail el formato de almacenado es mbox: básicamente para cada usuario, todos sus correos se almacenan en un archivo que se llama igual que el usuario en la carpeta /var/spool/mail.

En  este caso particular el utilitario clamscan, borra el mailbox completo y no solamente los emails infectados que contenga.

Cree un utilitario que usa básicamente dos librerías perl: una para trabajar con el mailbox y otra para conectarse con el clamd, y que limpia correcta y eficientemente los mailboxes.

Haz click para descargar cleanmbox.pl

Ir al principio



Hans Poo, Marzo 2005, hans@welinux.cl, Actualizado Octubre 2004.