Mostrando entradas con la etiqueta tutorial. Mostrar todas las entradas
Mostrando entradas con la etiqueta tutorial. Mostrar todas las entradas

02 abril 2012

Programando tareas en Lignux con at, batch, cron y anacron

Si queremos programar tareas en Lignux, contamos con diversas herramientas que permiten automatizarlas. Podemos dividirlas en estas categorías: herramientas para ejecutar tareas regularmente, en un momento concreto o cuando el sistema tenga un nivel de carga bajo.

Para ejecutar tareas en un momento concreto, contamos con el comando at; para ejecutarlas cuando nos encontramos con un nivel de carga bajo, podemos utilizar también at o batch, siendo ambos comandos del mismo paquete; y para ejecutarlas de forma regular, utilizaremos cron. Los tres comandos se instalarán, al menos en Debian, si instaláis el paquete lsb-core.

Programando tareas con at

Para programar tareas ejecutaremos el comando at con una fecha del estilo HH:MM [DD.MM.AAAA] [ + desfase], donde el desfase puede ser un número y una unidad (minute, hour, day, week, month, year); los [ ] indican que el parámetro es opcional. También podemos usar horas con formato HH AM/PM.

Tras ejecutar el comando, se nos mostrará un prompt donde podremos introducir los comandos; para finalizar, pulsamos Ctrl+D. También es posible almacenar los comandos a ejecutar, separados por saltos de línea, en un fichero de texto y pasárselo al at con la opción -f archivo.

También es posible añadir los tareas a colas de prioridades con la opción -q letra, donde la letra puede ser una letra de la a a la z, mayúscula o minúscula; nos centraremos en la minúsculas. Con las letras minúsculas podemos establecer la prioridad (nice) que tendrá el proceso cuando se ejecute. Así, los procesos en la cola a (por defecto), se ejecutarán con un valor nice 2, mientras que los de la cola b, c, d... se ejecutarán con un valor nice de 4, 6, 8, etc. respectivamente, obteniendo una prioridad inferior. Cabe decir que la cola b es una cola especial de batch que explicaremos más adelante.

Finalmente, podemos gestionar las tareas con los siguientes comandos:

$ at -l # lista las tareas programadas, cuando se ejecutarán y en qué cola están
$ at -c id # muestra los comandos a ejecutar por la tarea con identificador id
$ at -r id # elimina la tarea con identificador id

Programando tareas con batch

Utilizaremos batch cuando queramos programar tareas para cuando el sistema se encuentre con un bajo nivel de carga. Funciona de la misma forma que at: ejecutamos batch y nos aparecerá un prompt donde podemos escribir los comandos a ejecutar. A diferencia de at, batch no acepta ninguna opción ni flag.

Gestionaremos las tareas programadas con los mismos comandos que hemos visto con at.

También es posible programar tareas en modo batch con at utilizando la cola b o las colas con letra mayúscula; de esta forma, las tareas encoladas en la cola A se ejecutarán a partir del momento que hayamos especificado con un valor nice 2, siguiendo el mismo mecanismo que hemos explicado antes.

Programando tareas con cron

Con cron podemos programar tareas que se tienen que ejecutar de forma periódica.

Dado que por defecto cron viene configurado para que sólo pueda ser ejecutado por el usuario root, debemos editar, o crear si fuese necesario, el archivo /etc/cron.allow y añadir los usuarios, uno por línea, que queremos que puedan programar tareas con cron.

Una vez habilitado el uso de cron, podemos editar las tareas a programar con el comando crontab -e que abrirá un editor en el que os mostrará información y una de sus últimos comentarios será así:

# m h  dom mon dow   command

Esta es una guía de cómo debemos introducir los parámetros que definen cuando se tiene que ejecutar un comando: la m y la h es el minuto y la hora en que se ejecutará el comando; un * indica en cualquier minuto u hora. Dom significa day of month, es decir, que día del mes se ejecutará el comando, del 1 al 31; mon se refiere a month, el mes, del 1 al 12; dow significa day of week, qué día de la semana se ejecutará, del 1 al 7; finalmente, command es el comando que se ejecutará. En cualquiera de los parámetros, exceptuando el parámetro de comando, soporta el asterisco (*), que viene a decir que es indiferente; sin embargo, podemos escribir */10, que si lo ponemos en el campo de minutos, significará que el comando se tiene que ejecutar cada 10 minutos. También podemos hacer una lista de números separados por comas (10, 20, 30, etc.), o un intervalo (1-5 en dow significaría de lunes a viernes).

Una vez acabada la edición, guardamos y salimos de crontab; a partir de este momento el comando se ejecutará siempre que la fecha y la hora coincida con la establecida.

Programando tareas con anacron

No lo he mencionado anteriormente, pues no es instala por defecto con el paquete lsb-core, además de tener una finalidad un tanto peculiar: anacron se utiliza para programar tareas que se tienen que ejecutar a partir de cierto periodo de tiempo, pero durante ese periodo de tiempo nuestro sistema no tiene porqué estar encendido. Por ejemplo, si yo quiero realizar una copia de seguridad como máximo cada 7 días, pero no puedo asegurar que mi PC esté siempre encendido a una hora concreta, no podría programarlo con cron, sino que debería hacerlo con anacron.

De esta manera, cada vez que ejecutemos anacron, comprobará la última ejecución de la tarea, y si es superior a los días indicados, volverá a ejecutarla.

Si queremos instalar anacron, ejecutaremos como root:

# aptitude install anacron

Por defecto, anacron sólo es ejecutado por el usuario root, y no tiene un sistema similar al de cron, que nos permita autorizar la ejecución periódica, entre otras cosas porque anacron no es un demonio que se mantenga comprobando el estado de las tareas, sino que se ejecuta al inicio del sistema o una vez al día mediante cron.

Sin embargo, es posible indicarle a anacron una configuración de usuario y un directorio donde almacenará la información sobre las tareas ejecutadas:

/usr/sbin/anacron -t anacrontab -S directorio

Tenemos que tener en cuenta que si ejecutamos anacron de esta manera, deberemos ejecutarlo de forma manual o bien programando una tarea con cron cada pocas horas o bien como una tarea al inicio de sesión con, por ejemplo, gnome-session-properties; o bien editar como usuario root el archivo /etc/anacrontab.

Veamos ahora cómo se estructura un archivo de configuración de anacron, conocido como anacrontab, en un comentario:

# period  delay  job-identifier  command

Como se puede ver, el primer elemento es el periodo, en días, en el cual queremos que se repita la tarea; el periodo puede ser también la palabra @monthly, que hará que se ejecute una vez al mes, independientemente de los días que tenga ese mes. El delay indica el timepo de espera en minutos que tardará en ejecutarse la tarea; esto se usa para no ejecutar de golpe todos las tareas o para dar tiempo a que el sistema acabe de iniciarse. El job-identifier es una palabra que identifica la tarea de forma única, como podria ser mantenimiento.diario; anacron utiliza este identificador para gestionar la información que guarda en el directorio especificado por la opción -S, o /var/spool/anacron por defecto. Finalmente, el último parámetro es el comando a ejecutar.

Sobre la ejecución de los comandos

En todos los casos mencionados, se ejecutará como intérprete el shell en /bin/sh (se puede reasignar utilizando la variable SHELL) y puede que las variables de entorno no estén asignadas (en el caso de cron debes establecer la variable PATH si quieres asegurarte de que no tendrás problemas); se pueden utilizar scripts y se pueden asignar variables en los comandos introducidos en at y batch, de la misma forma que haríamos si escribiésemos un script, o bien en el mismo archivo de configuración con cron y anacron.

Un saludo,
Morpheus

06 febrero 2012

GIMP: Como crear la silueta de un mapa

Hace algún tiempo me puse a trabajar en una nueva partida de rol; quería generar para ella un mapa de una región de la ambientación que pareciese real, en lo que a la silueta de este se refiere. Para ello, traté de encontrar algún tutorial que me permitiese generarlo en GIMP de una manera más o menos sencilla; sin embargo, no conseguí encontrar nada parecido a lo que estaba buscando, más que este tutorial, pero que seguía sin ser lo que yo buscaba.

Así pues, basándome en el tutorial encontrado, traté de generar un mapa utilizando el GIMP, y creo que he encontrado una manera bastante aceptable de generar la silueta de un mapa, además de ser bastante sencilla. Os la explico a continuación:

  1. Abrimos un archivo nuevo en GIMP utilizando una resolución lo suficientemente alta, pues lo que generemos seguramente sea mucho más pequeño de lo que sea el lienzo.

  2. Llenamos el lienzo con Filtros/Renderizado/Ruido Sólido.

    Los parámetros que pondremos serán en función de lo que deseemos:

    • Detalle: Mientras más grande sea, más escarpada será la silueta que obtendremos.
    • Tamaño X/Y: Lo estableceremos en función de si queremos contar con más islas pequeñas o más islas/continentes grandes.
    • Semilla: Sirve para generar el ruido. Podemos ir generando semillas hasta que encontremos una que nos agrade.
  3. Utilizamos la herramienta Umbral (Herramientas/Herramientas de color/Umbral) para generar las partes o las islas que compondrán el mapa.

  4. Eliminamos el color de fondo (en mi caso, blanco), convirtiéndolo en transparente con la herramienta Colores/Color a Alfa.

  5. Recortamos las siluetas que nos gusten.

  6. Las escalamos, rotamos y superponemos como nosotros queramos para generar las islas o continentes.

  7. Repetimos tantas veces como queramos los pasos del 2 al 6 en distintas capas para generar el mapa que más nos guste.

De esta forma, te quedará algo así, que podrás utilizar para rellenar con los elementos que más te gusten (montañas, árboles, ríos, etc.).

Para rellenar los mapas puedes utilizar los pinceles para GIMP overland map brushes y Tolkien-Style Map Brushes o bien el conjunto de SVG rpg map.

Finalmente, os adjunto el archivo XFC del mapa que hemos creado en este tutorial.

Un saludo,
Morpheus

01 febrero 2012

Establecer distribución de teclado por consola

Si queréis cambiar la distribución de vuestro teclado por consola hay dos formas.

La primera es con una terminal que corre sobre el servidor gráfico, es decir, sobre las X con gnome-terminal, xterm, konsole, etc.:

setxkbmap cc

cc es el código del país, en mi caso, es para el teclado en español.

Para cambiarlo sin X, es decir, en una terminal virtual como tty1 (Ctrl+Alt+F1):

loadkeys es

En este caso, he puesto directamente es, pero podria ser el código de otro pais, como us para el teclado americano. Cabe decir que este comando se tiene que ejecutar con permisos de administrador o root.

Podéis ver los códigos de distribución de teclado disponibles para setxkbmap en los directorios bajo /usr/share/X11/xkb/keymap/ y para loadkeys en /usr/share/kbd/keymaps.

Un saludo,
Morpheus

05 enero 2012

Ping usando NetBios y mDNS

Cuando usaba Ubuntu, o Mint basado en Ubuntu, podía administrar mi red local sin tener que preocuparme por si los PCs tenían asignadas direcciones IP estáticas o dinámicas asignadas por DHCP por el router, ya que podía acceder mediante las direcciones nombre.local; este formato es el Fully Qualified Domain Name o FQDN.

Cuando me cambié a Debian, por algún motivo el mismo sistema no funcionaba. Pero hoy, tras leer este enlace que habla sobre Samba (el sistema que implementa en Unix el protocolo utilizado para los grupos de trabajo de Windows), mencionaban una pista que me ha sido de ayuda.

La solución no estaba en utilizar el sistema NetBios (el utilizado por Samba), sino el sistema mDNS, que precisamente es el que utilizaba anteriormente. Sin embargo, he configurado ambos sistemas.

Para usar NetBios y mDNS es necesario instalar los siguientes paquetes:

aptitude install libpam-winbind libnss-winbind libnss-mdns

Actualización 1/06/2012: Parece que ahora, en Debian wheezy, la libnss_wins se encuentra en el paquete libnss-winbind, por tanto, ya no es necesario instalar el paquete libpam-winbind. En caso de que no estéis seguros de en qué paquete pueda estar la biblioteca, podéis instalar el paquete apt-file, que te permite buscar archivos incluso en aquellos paquetes que no tienes instalados.

Una vez instalados estos paquetes, que instalarán las bibliotecas libnss_wins y libnss_mdns, necesarias para poder configurar correctamente el sistema, editaremos el archivo /etc/nsswitch.conf y añadiremos, en orden de preferencia, a la línea hosts: los sistemas wins y mdns que queremos utilizar para resolver los nombres, quedando así:

hosts: files wins mdns dns

De esta forma, el sistema primero buscará en el archivo /etc/hosts, si no lo encuentra utilizará el sistema NetBios, el mDNS y, finalmente, si no lo encuentra recurrirá al servidor DNS configurado.

Con todo ello, podemos hacer ping utilizando el nombre de red del PC:

ping nombre

O bien nombre.local:

ping nombre.local

Un saludo,
Morpheus

06 octubre 2011

Mostrar http en Firefox 7

Saludos,

Acabo de actualizar el Iceweasel, el Firefox en Debian con el nombre y los iconos cambiados, a la versión 7, y como ya andaba esperando, ha desaparecido el protocolo http de la barra de direcciones (el https y otros, como el ftp, siguen apareciendo).

Si la costumbre os puede tanto como a mí, podéis volver a ver el http en vuestra barra de direcciones configurándolo en la dirección about:config, poniendo el siguiente parámetro a false:

browser.urlbar.trimURLs

En la fuente de la cual he extraído la información también se menciona como eliminar el resaltado del dominio de la barra de direcciones; para ello tenéis que poner también a false lo siguiente:

browser.urlbar.formatting.enabled

Fuente: Techdows

Un saludo,
Morpheus

08 septiembre 2011

Poniendo a punto el YP-CP3 en Lignux

Hace poco, mi antiguo reproductor portátil (aka MP3), un Sansa Clip+ de 4GB dejó de funcionar; por suerte, me entraba en garantía y Fnac me hizo una tarjeta regalo por el mismo precio que este. Por desgracia, fue una gran pérdida, pues ese Sansa tenía ampliación de memoria por tarjeta microSD y reproducía Flac y Ogg (Vorbis).

Tras recibir la tarjeta, me puse a buscar un reproductor que tuviese las mismas características o similares en el catálogo de Fnac; sin embargo, lo único que me aparecían eran Samsung's, pero anteriormente ya había tenido uno y me había dado ciertos problemillas, además de que al final el conector USB, que estaba ensamblado dentro del propio artefacto, como los reproductores que estaba encontrando en el catálogo, dejó de funcionar bien y varias veces casi lo rompo debido a un golpe accidental.

Visto que no podía encontrar ningún reproductor que se ajustase a mis necesidades, envie un correo electrónico a los 3 Fnac's que hay en Barcelona, y me consiguieron encontrar una buena alternativa que se ajustase a mis necesidades, que no se encontraba en el catálogo, pero con el que contaban, y seguramente cuenten aún, con existencias en el Fnac de l'Illa Diagonal: el Samsung YP-CP3 (especificaciones), un reproductor de vídeo y audio (aka MP4), que acepta Flac y Vorbis y además se conecta al PC mediante un cable, con lo cual no corre el peligro de estropearse como mi anterior reproductor Samsung. Como podréis imaginar, es el reproductor que finalmente he adquirido.

Este reproductor, a la par que interesante, cuenta con cierta falta de soporte en Lignux; ya que pese a que se conecta sin ningún problema, no acaba de sincronizarse bien con Rhythmbox, y el programa que cabría instalar para poder sacarle toda la potencia (Emodio) requiere de Internet Explorer y el reproductor Windows Media, por lo tanto no se puede instalar mediante Wine. Pues bien, este artículo va a tratar de los pequeños parches que he realizado para poder utilizar sin problema este, de momento, gran reproductor.

Sincronizando con Rhythmbox

En primer lugar, quería aprovechar la capacidad de las últimas versiones de Rhythmbox de sincronizarse con los reproductores multimedia, y para ello apliqué un viejo truco, que es añadir un archivo con nombre .is_audio_player en la raíz del sistema de ficheros del dispositivo; sin embargo, este archivo puede especificar más información sobre el reproductor que quizás, y en mi caso, en una Debian Testing, no se encuentren en el paquete media-player-info, quedando mi archivo .is_audio_player de la siguiente forma:

output_formats=audio/mpeg,audio/x-ms-wma,application/ogg,audio/ogg,audio/flac,audio/x-wav,image/jpeg,text/plain,video/x-ms-wmv,video/x-msvideo
input_formats=audio/mpeg
audio_folders=Music/

(Fuente: FAQ Rhythmbox)

Transfiriendo las listas de reproducción

Una vez que Rhythmbox reconoce bien el reproductor y les transfiere las pistas en los formatos que acepta (si no, primero las transformaría a mp3), sería interesante conseguir que las listas de reproducción definidas en Rhythmbox pudiesen reproducirse también en el reproductor; sin embargo, el CP3 sólo acepta listas en formato SPL (un formato propio de Samsung) y WPL, el formato de Windows Media, pero Rhythmbox deja las listas en la raíz del sistema en formato PLS.

Indagando un poco por internet, encontré, entre otros, que el proyecto libmtp ya había programado una utilidad para este tipo de listas; sin embargo, el reproductor no se conecta mediante MTP, así que no podía servirme de mucho. Pese a esto, también encontré cierta información en los foros de anythingbutipod (1 y 2), obteniendo jutno con un par de pruebas las características necesarias para preparar un archivo SPL que reconociese el reproductor:

  • Las primeras dos líneas deben ser la especificación de que se trata de un archivo SPL y la versión del formato (1.00 o 2.00 según el firmware), junto con un espacio en blanco:
    SPL PLAYLIST
    VERSION 1.00
  • Cada canción debe especificarse en una línea con su ruta siguiendo el siguiente formato: \Music\Ruta\Archivo 1.mp3
  • Un espacio en blanco y una línea indicando que se acabado la lista:

    END PLAYLIST
  • El archivo debe estar codificado en formato UTF-16
  • Los saltos de línea deben estar especificados en formato CR+LF (Windows)

Siguiendo estas reglas, obtenemos un archivo SPL válido; ahora lo que nos falta es convertir las listas en PLS a este formato. Para tal fin, yo he hecho un pequeño script en Shell y AWK, que he bautizado como pls2spl.sh y que, dada la ruta donde se encuentra el reproductor, lee todas las listas sincronizadas por Rhythmbox, que por defecto se guardan en la raíz, y una vez transformadas las mueve a la carpeta Playlists:

#!/bin/bash

# Script AWK que transforma un archivo PLS al formato SPL
awk='BEGIN {
FS="/";
OFS="\\";
print "SPL PLAYLIST\nVERSION 2.00\n";
}

/[Ff]ile([0-9]+)=/ { # Sólo nos interesan las rutas de los archivos
printf "%s", gensub("[Ff]ile([0-9]+)=", OFS, 1, $1);
for(i=2; i <= NF; i++) printf "\\%s", $i;
printf "\n";
}

END {
print "\nEND PLAYLIST";
}'

TMPFILE=/tmp/spl;

# Función para obtener el nombre del archivo en una ruta
function filename() {
echo echo $i | awk 'BEGIN{FS="/"} {print $NF}';
}

readonly DIR=Playlists; # Donde se guardan las listas de reproducción

#Comprobaciones iniciales
if (( $# < 1 )); then
echo "Por favor, especifica la ruta al dispositivo (ex: /media/ypcp3)";
exit 1;
elif (( $# > 1 )); then
echo "Sólo puedes especificar un dispositivo";
exit 1;
fi

# Preparamos el awk para transformar el archivo pls
awkfile=pls2spl.awk;
echo "$awk" > $awkfile;

# Nos aseguramos de que $DEVICE no tenga un barra al final
DEVICE=$(echo $1 | awk '{if ( substr($0, length($0),1) == "/") print substr($0, 0, length($0)-1); else print $0 }' );

IFS=$(echo -e "'\n'");
for i in $(ls -1 $DEVICE/*.pls); do
awk -f $awkfile $i > $TMPFILE;

# Transformamos a CRLF (Windows) y a Unicode-16
sed s/$/'\r'/ $TMPFILE | iconv -f UTF-8 -t UTF-16 -o $DEVICE/$DIR/$(filename $i | sed s/.pls/.spl/) -;

echo -n "Se ha convertido ";
filename $i;

rm $TMPFILE;
rm $i;
done

rm $awkfile;

He de hacer notar que en el archivo .is_audio_player se podría haber especificado en qué carpeta debe guardar Rhythmbox las listas. Si lo hiciésemos, Rhythmbox especificaría la ruta del archivo de la forma file:///media/cp3/Music/archivo%201.mp3, lo cual no es una ruta válida para el formato SPL; debido a esto, he preferido mantener el comportamiento por defecto de Rythmbox, lo cual facilita la conversión, evitando el texto escapado, es decir, las construcciones del tipo %20.

Convirtiendo los vídeos

Pese a que según las especificaciones de la página web el reproductor acepta WMV, MPEG4, AVI, MP4, ASF, según las especificaciones del manual de usuario el reproductor sólo reproduce los siguientes formatos de vídeo:

AVI/SVI: MPEG-4 Perfil simple de vídeo (640x480, 800kbps), MP3 Audio
RM/RMVB: Vídeo Real Media (640x480, 800kbps), Real Audio
WMV: Vídeo WMV9 (640x480, 500kbps), Audio WMA

Con tal de poder transformar los vídeos con comodidad al formato AVI que especifica, y sobre todo poder realizar el escalado de forma automática manteniendo la proporción, he realizado un pequeño script para Avidemux valiéndome de lo que he aprendido con la entrada anterior; además, este script lo he añadido como ajuste personalizado dentro de mi carpeta personal, tal y como explican en la wiki de Avidemux. En el script, que os muestro a continuación, podréis notar que he realizado ciertas operaciones para poder realizar el escalado; si es necesario, podéis encontrar una gran referencia y fuente de información sobre javascript en el w3schools.

//AD  <- Needed to identify//
//--automatically built--

var app = new Avidemux();

//** Video **
// ** Preparing
var maxw=640, maxh=480; // Max permited by device
var resx=400, resy=240, dratio=resx/resy; // dratio=15/9=5/3 (Display)

//** Postproc **
app.video.setPostProc(3,3,0);
app.video.fps1000 = 25000;

//** Scaling **
var width=app.video.width;
var height=app.video.height;
var vratio=width/height;

if (width >= height) {
width=resx=maxw; // Video will fit up to device
height=Math.round((width/vratio)/2)*2; // Values must be pair
resy=resx/dratio;

if (height > resy || height > maxh) {
height=resy; // To preserve display and video ratio
width=Math.round((height*vratio)/2)*2; // Values must be pair
resx=resy*dratio;
}
}
else { // Improbably
height=resy=maxh;
width=Math.round((height*vratio)/2)*2;
resx=resy*dratio;

if (width > resx || width > maxw) {
width=resx;
height=Math.round((width/vratio)/2)*2;
resy=resx/dratio;
}
}

app.video.addFilter("resize","w="+width,"h="+height,"algo=1");

// ** Filling with black to preserve aspect ratio (padding)

var padw=(resx-width)/2, padh=(resy-height)/2;

app.video.addFilter("addblack","left="+padw,"right="+padw,"top="+padh,"bottom="+padh);

//** Video Codec conf **
app.video.codecPlugin("92B544BE-59A3-4720-86F0-6AD5A2526FD2", "Xvid", "CBR=800", "?xml version='1.0'?><XvidConfig><presetConfiguration><name><default></name><type>default</type></presetConfiguration><XvidOptions><threads>0</threads><vui><sarAsInput>true</sarAsInput><sarHeight>1</sarHeight><sarWidth>1</sarWidth></vui><motionEstimation>high</motionEstimation><rdo>dct</rdo><bFrameRdo>false</bFrameRdo><chromaMotionEstimation>true</chromaMotionEstimation><qPel>false</qPel><gmc>false</gmc><turboMode>false</turboMode><chromaOptimiser>false</chromaOptimiser><fourMv>false</fourMv><cartoon>false</cartoon><greyscale>false</greyscale><interlaced>none</interlaced><frameDropRatio>0</frameDropRatio><maxIframeInterval>300</maxIframeInterval><maxBframes>2</maxBframes><bFrameSensitivity>0</bFrameSensitivity><closedGop>false</closedGop><packed>false</packed><quantImin>1</quantImin><quantPmin>1</quantPmin><quantBmin>1</quantBmin><quantImax>31</quantImax><quantPmax>31</quantPmax><quantBmax>31</quantBmax><quantBratio>150</quantBratio><quantBoffset>100</quantBoffset><quantType>mpeg</quantType><intraMatrix><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value><value>8</value></intraMatrix><interMatrix><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value><value>1</value></interMatrix><trellis>true</trellis><singlePass><reactionDelayFactor>16</reactionDelayFactor><averagingQuantiserPeriod>100</averagingQuantiserPeriod><smoother>100</smoother></singlePass><twoPass><keyFrameBoost>10</keyFrameBoost><maxKeyFrameReduceBitrate>20</maxKeyFrameReduceBitrate><keyFrameBitrateThreshold>1</keyFrameBitrateThreshold><overflowControlStrength>5</overflowControlStrength><maxOverflowImprovement>5</maxOverflowImprovement><maxOverflowDegradation>5</maxOverflowDegradation><aboveAverageCurveCompression>0</aboveAverageCurveCompression><belowAverageCurveCompression>0</belowAverageCurveCompression><vbvBufferSize>0</vbvBufferSize><maxVbvBitrate>0</maxVbvBitrate><vbvPeakBitrate>0</vbvPeakBitrate></twoPass></XvidOptions></XvidConfig>");

//** Audio **
app.audio.reset();
app.audio.codec("Lame",128,20,"80 00 00 00 01 00 00 00 01 00 00 00 09 00 00 00 00 00 00 00 ");
app.audio.normalizeMode=0;
app.audio.normalizeValue=0;
app.audio.delay=0;
app.audio.mixer="NONE";
app.setContainer("AVI");
setSuccess(1);
//app.Exit();

//End of script

Sin embargo, pese a haber realizado este script, y siendo Avidemux un gran programa, como mínimo de transcodificación de archivos; me he encontrado con vídeos que sufrían ciertos problemas al ser transcodificados, como por ejemplo un desfase en el audio respecto al vídeo, que han hecho que utilice también otro gran programa: ffmpeg (sí, sé que mencioné que lo encontraba un poco complicado, pero para usarlo habitualmente; una vez establecidos unos parámetros que no van a variar, no lo he encontrado tan complicado). Pues con tal de realizar el mismo trabajo que con Avidemux, he hecho otro script (sí, también sé que últimamente estoy algo pesado con ellos) que se encarga también de mantener la proporción del archivo a la hora de escalarlo:

#!/bin/bash

# Constantes
readonly resx=400;
readonly resy=240;
readonly maxw=640;
readonly maxh=480;

IFS=$(echo -e "\n");

# Comprobaciones
if (( $# < 1 )); then
echo "Debes especificar los vídeos a codificar";
exit 1;
fi


# Funciones

# Recoge información sobre el vídeo en $width y $height
# $1 -> Ruta al archivo de vídeo
function getInfo() {
local video=$(ffmpeg -i $1 2>&1 | grep -iw "video:")
local size=$(echo $video | egrep -o "[0-9]+x[0-9]+");

width=$(echo $size | cut -d"x" -f1);
height=$(echo $size | cut -d"x" -f2);
}

# $1 -> Elemento a dividir
# $2 -> Width
# $3 -> Height
function divPerRatio() {
echo $(( ($1 * $3) / $2 ));
}

# $1 -> Elemento a multiplicar
# $2 -> Width
# $3 -> Height
function mulPerRatio() {
echo $(( ($1 * $2) / $3 ));
}

# Pre: haber obtenido la información de $width y $height (con getInfo)
# Retorna el tamaño del vídeo ($x $y) y la resolución en pantalla ($rx $ry)
function getRes() {
if (($width >= $height)); then
x=$maxw;
rx=$x; # Para encajar el vídeo en el dispositivo
y=$(divPerRatio $x $width $height);
ry=$(divPerRatio $rx $resx $resy);

if (($y > $ry || $y > $maxh)); then
y=$ry; # Para conservar la proporción del dispositivo y el vídeo
x=$(mulPerRatio $y $width $height);
rx=$(mulPerRatio $ry $resx $resy);
fi
else # Improbable
y=$maxh;
ry=$y;
x=$(mulPerRatio $y $width $height);
rx=$(mulPerRatio $ry $resx $resy);

if (($x > $rx || $x > $maxw)); then
x=$rx;
y=$(divPerRatio $x $width $height);
ry=$(divPerRatio $rx $resx $resy);
fi
fi
}


# Main
for (( f=1 ; f <= $# ; f++ )); do
file=${!f};

# Añadimos extension si no la tiene
if ! echo $file | egrep "\..{1,3}$" > /dev/null; then
ln $file $file.ln;
file=$file.ln;
LKD=1;
fi

getInfo $file;
getRes;

posx=$(( ($rx - $x)/2 ));
posy=$(( ($ry - $y)/2 ));

output=$(echo $file | sed -r s/'\..{1,3}$'/.cp3.avi/);

# Ejecución
ffmpeg -i $file -ab 128k -acodec libmp3lame -vcodec libxvid -b 800k -r 25 -s ${x}x$y -vf pad="$rx:$ry:$posx:$posy" $output;

# Borramos archivo con extensión
if (( $LKD )); then
LKD=0;
rm $file;
fi
done

Como podréis comprobar, transforma todo aquellos vídeos que se le pasen como argumento al script; por ejemplo:

./ypcp3.ffmpeg.sh video1.mp4 video2 video3.wmv

Pues hasta aquí hemos llegado. Espero que este artículo os haya sido de ayuda, y si no, a mí me ha resultado útil para mantener estos scripts y esta información como apunte en mi blog.

Un saludo,
Morpheus

01 septiembre 2011

Cambiando el tamaño de tus vídeos con Avidemux

Siguiendo en la línea de la entrada anterior, voy a seguir en esta entrada explicando cómo transformar diversos vídeos (en alta definición) en vídeos más pequeños pero con una calidad aceptable; en mi caso, y en mi percepción, se trata de un resultado más que aceptable.

Para ello, he utilizado Avidemux; sí, sé que podría haber usado MEencoder (de MPlayer) o FFmpeg, pero aunque ya los he utilizado alguna vez, me resulta difícil configurar todas las opciones del codificador mediante la línea de comandos. Entonces os preguntaréis: ¿cómo piensas hacerlo con Avidemux? Pues la respuesta es a medias.

Pero primero, vayamos por pasos. Antes de nada, debemos tener instalado avidemux (junto a su interfaz de línea de comandos, CLI):

# aptitude install avidemux-cli avidemux

Ahora, abrimos uno de los vídeos que queramos recodificar con él. Una vez hecho esto, configuramos todos los parámetros de vídeo y audio que querremos que tengan nuestros vídeos; en mi caso, escalar los vídeos a la mitad de su tamaño mediante el filtro dimensionar y ponerle una tasa de bits constante de 1500 kbps (que ya está puesto por defecto):

Tras ello, vamos a Archivo -> Guardar Proyecto como... y lo guardamos con la extensión .js, pues se guardarán los parámetros de nuestro proyecto como un archivo en javascript. Una vez hecho esto, abrimos el archivo con un editor de texto y eliminamos las siguientes líneas:

// 01 videos source 
app.load("/ruta/al/video.ext");
//01 segments
app.clearSegments();
app.addSegment(0,0,3017);
app.markerA=0;
app.markerB=3016;

Eliminar estas líneas es necesario ya que son configuraciones dependientes del vídeo que hemos abierto para guardar el script. Los marker son las marcas que definen el segmento de vídeo que vamos a transformar, y como comprenderéis, son diferentes para cada vídeo, motivo por el cual no se deben establecer aquí; lo mismo para la ruta.

Una vez creado y modificado el script de configuración, es hora de utilizarlo; para ello, debemos ejecutar la siguiente línea:

avidemux2_cli --force-alt-h264 --load video.ext --run script.js --save video.res.ext --quit

Los parámetros utilizados son:

--force-alt-h264
Para que en los vídeos que estén codificados con H.264 se habran correctamente sin necesidad de preguntar al usuario; lo pongo porque la mayoría de las cámaras gravan en MOV, que no deja de ser, en la gran mayoría de veces, un contenedor de este codec.
--load
Para cargar el vídeo que deseamos.
--run
Para ejecutar el script que hemos guardado anteriormente.
--save
Para indicar dónde debe guardar el vídeo recodificado.
--quit
Para finalizar el programa en cuanto haya acabado.

Una vez contamos con el script y conociendo cuál es el comando a ejecutar, para recodificar diversos vídeos deberíamos ejecutar el siguiente comando (en bash):

for i in `ls *.ext`; do
output=$(echo $i | sed s/.ext/sca.ext/);
avidemux2_cli --force-alt-h264 --load $i --run script.js --save $output --quit;
done

De esta forma, se transformarán todos los archivos con la extensión ext utilizando la configuración almacenada en script.js.

Si queréis realizar algo un tanto más complejo, podéis modificar parte del javascript, como yo he hecho a la hora de establecer el escalado, haciendo que el vídeo se escale en la proporción que hayas establecido en la variable scale:

//** Scaling **
var scale=1/2;
var width=app.video.width*scale;
var height=app.video.height*scale;

displayInfo("Video will be scaled to "+width+"x"+height+" pixels.");

app.video.addFilter("resize","w="+width,"h="+height,"algo=1");

Si aún así queréis hacerlo todo más compacto, podéis incluir el propio javascript dentro de otro script en bash. Os presento como ejemplo el que he realizado para convertir mis vídeos (atención a las comillas de las variables y los \' que he puesto en la versión del xml):

#!/bin/bash

DIR=/tmp/sca
SCRIPT='//AD <- Needed to identify//
//--automatically built--

var app = new Avidemux();

//** Video **
//** Postproc **
app.video.setPostProc(3,3,0);

app.video.fps1000 = 25000;

//** Filters **

//** Scaling **
var scale=1/2;
var width=app.video.width*scale;
var height=app.video.height*scale;

displayInfo("Video will be scaled to "+width+"x"+height+" pixels.");

app.video.addFilter("resize","w="+width,"h="+height,"algo=1");

//** Video Codec conf **
app.video.codecPlugin("32BCB447-21C9-4210-AE9A-4FCE6C8588AE", "x264", "2PASSBITRATE=1500", "<?xml version='\'1.0\''?><x264Config><presetConfiguration><name>&lt;default&gt;</name><type>default</type></presetConfiguration><x264Options><fastFirstPass>true</fastFirstPass><threads>0</threads><deterministic>true</deterministic><sliceThreading>false</sliceThreading><threadedLookahead>-1</threadedLookahead><idcLevel>-1</idcLevel><vui><sarAsInput>true</sarAsInput><sarHeight>1</sarHeight><sarWidth>1</sarWidth><overscan>undefined</overscan><videoFormat>undefined</videoFormat><fullRangeSamples>true</fullRangeSamples><colorPrimaries>undefined</colorPrimaries><transfer>undefined</transfer><colorMatrix>undefined</colorMatrix><chromaSampleLocation>0</chromaSampleLocation></vui><referenceFrames>3</referenceFrames><gopMaximumSize>250</gopMaximumSize><gopMinimumSize>0</gopMinimumSize><scenecutThreshold>40</scenecutThreshold><periodicIntraRefresh>false</periodicIntraRefresh><bFrames>3</bFrames><adaptiveBframeDecision>1</adaptiveBframeDecision><bFrameBias>0</bFrameBias><bFrameReferences>strict</bFrameReferences><loopFilter>true</loopFilter><loopFilterAlphaC0>0</loopFilterAlphaC0><loopFilterBeta>0</loopFilterBeta><cabac>true</cabac><openGop>disabled</openGop><interlaced>disabled</interlaced><constrainedIntraPrediction>false</constrainedIntraPrediction><cqmPreset>flat</cqmPreset><intra4x4Luma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></intra4x4Luma><intraChroma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></intraChroma><inter4x4Luma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></inter4x4Luma><interChroma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></interChroma><intra8x8Luma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></intra8x8Luma><inter8x8Luma><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value><value>16</value></inter8x8Luma><analyse><partitionI4x4>true</partitionI4x4><partitionI8x8>true</partitionI8x8><partitionP8x8>true</partitionP8x8><partitionP4x4>false</partitionP4x4><partitionB8x8>true</partitionB8x8><dct8x8>true</dct8x8><weightedPredictionPframes>smart</weightedPredictionPframes><weightedPrediction>true</weightedPrediction><directPredictionMode>spatial</directPredictionMode><chromaLumaQuantiserDifference>0</chromaLumaQuantiserDifference><motionEstimationMethod>hexagonal</motionEstimationMethod><motionVectorSearchRange>16</motionVectorSearchRange><motionVectorLength>-1</motionVectorLength><motionVectorThreadBuffer>-1</motionVectorThreadBuffer><subpixelRefinement>7</subpixelRefinement><chromaMotionEstimation>true</chromaMotionEstimation><mixedReferences>true</mixedReferences><trellis>finalMacroblock</trellis><fastPSkip>true</fastPSkip><dctDecimate>true</dctDecimate><psychoRdo>1</psychoRdo><psychoTrellis>0</psychoTrellis><noiseReduction>0</noiseReduction><interLumaDeadzone>21</interLumaDeadzone><intraLumaDeadzone>11</intraLumaDeadzone></analyse><rateControl><quantiserMinimum>10</quantiserMinimum><quantiserMaximum>51</quantiserMaximum><quantiserStep>4</quantiserStep><maximumConstantRateFactor>0</maximumConstantRateFactor><averageBitrateTolerance>1</averageBitrateTolerance><vbvMaximumBitrate>0</vbvMaximumBitrate><vbvBufferSize>0</vbvBufferSize><vbvInitialOccupancy>0.9</vbvInitialOccupancy><ipFrameQuantiser>1.4</ipFrameQuantiser><pbFrameQuantiser>1.3</pbFrameQuantiser><adaptiveQuantiserMode>variance</adaptiveQuantiserMode><adaptiveQuantiserStrength>1</adaptiveQuantiserStrength><mbTree>true</mbTree><frametypeLookahead>40</frametypeLookahead><quantiserCurveCompression>0.6</quantiserCurveCompression><reduceFluxBeforeCurveCompression>20</reduceFluxBeforeCurveCompression><reduceFluxAfterCurveCompression>0.5</reduceFluxAfterCurveCompression></rateControl><accessUnitDelimiters>false</accessUnitDelimiters><spsIdentifier>0</spsIdentifier><sliceMaxSize>0</sliceMaxSize><sliceMaxMacroblocks>0</sliceMaxMacroblocks><sliceCount>0</sliceCount><hrd>none</hrd></x264Options></x264Config>");

//** Audio **
app.audio.reset();
app.audio.codec("Faac",128,4,"80 00 00 00 ");
app.audio.normalizeMode=0;
app.audio.normalizeValue=0;
app.audio.delay=0;
app.audio.mixer="NONE";
app.setContainer("MP4");
setSuccess(1);
//app.Exit();

//End of script';

SCRNAME="script.js";

echo "$SCRIPT" > $SCRNAME;

mkdir -p $DIR;
for i in `ls *.MOV`; do
output=$(echo $i | sed s/.MOV/sca.mp4/);
avidemux --nogui --force-alt-h264 --load $i --run $SCRNAME --save $DIR/$output --quit;
rm $DIR/$output.stat*;
done

rm $SCRNAME;

Fuentes utilizadas:

Pues eso es todo. Espero que este pequeño tutorial y el de la entrada anterior os hayan sido de ayuda; desde luego, para mí lo han sido, reduciendo unos 7,9 GB de imágenes y vídeos en tan solo 2,6 GB.

Espero no haber sido muy cargante con estas últimas entradas, pero quería conservar como apuntes este pequeño trabajo, que estoy bien seguro que me serán de utilidad en el futuro.

Un saludo,
Morpheus

30 agosto 2011

Cambiando el tamaño de tus fotos con Imagemagick

Después de las vacaciones es probable que te encuentres con un montón de fotos, como es mi caso; si además esas fotos están hechas con una cámara de 13 Mpx, te encontrarás con que tienes fotos que ocupan muchísimo espacio. Si la alta resolución de esas imágenes no es una prioridad para ti, pero sí para la persona que ha hecho las fotos, sino hubiese bastado con reducir la resolución en la propia cámara; puedes escalar esas fotografías para que ocupen menos utilizando el programa Imagemagick y el comando convert.

Para ello, lo primero que tenemos que hacer es instalar Imagemagick:

# aptitude install imagemagick

Una vez instalado, tan solo queda hacer uso del comando convert. La forma de usarlo es la siguiente:

convert [opciones] archivo_origen.extensión [opciones] archivo_destino.extensión

Para escalar las fotografías usaremos las opciones -resize WidthxHeight -quality 95. También se puede ser más concreto especificando la opción -filter filtro, puediendo encontrar información sobre ello aquí; aunque según esa información el filtro Cubic no funciona bien aumentando las imágenes, hasta ahora yo lo he usado sin notar ninguna pérdida considerable al reducirlas.

Sabiendo esto, podríamos escalar todas las imágenes con la opción -resize Widthx o -resize xHeight, que escalarían las imágenes preservando la proporción, usando el siguiente comando:

convert *.jpg -quality 95 -resize Widthx img.%03d.jpg

Por desgracia, esto haría que las fotos verticales fuesen más grandes que las horizontales, lo cual puede resultar un engorro. Para evitar esto, he hecho un pequeño script en Bash que hace uso de el comando identify, también del proyecto Imagemagick, que da información sobre las imágenes que especifiquemos. De esta forma, con el siguiente comando obtenemos la resolución de la imagen, que más tarde utilizaremos en el script:

identify -format "%wx%h" imagen.jpg

Finalmente, os dejo con el script, que básicamente transforma todas las imágenes del directorio donde se encuentra el script y las deja en el directorio DIR; las imágenes medirán como máximo WIDTH pixels de altura o de anchura:

#!/bin/bash

EXEC="convert"
EXECOPT="-quality 95 -filter Cubic -resize"
WIDTH=2856 # Esto da aproximadamente 6 Mpx para fotografías con una proporción 4:3
DIR=/tmp/sca

mkdir $DIR;
for i in `ls *.JPG`; do
aux=`identify -format "%wx%h" $i`;
w=`echo $aux | cut -f1 -d"x"`;
h=`echo $aux | cut -f2 -d"x"`;
resize=`echo ${WIDTH}x`;

if (( $w < $h )); then resize=`echo "x$WIDTH"`; fi

echo "$i: ${w}x$h -> $resize";
output=`echo $i | sed s/.JPG/sca.jpg/`;
eval $EXEC $i $EXECOPT $resize $DIR/$output;
done

Este script esta basado en otro que había hecho antes, que simplemente transforma el lado más pequeño de la imagen en el más grande:

#!/bin/bash

EXEC="convert"
EXECOPT="-quality 95 -filter Cubic -resize"

for i in `ls *.jpg`; do
aux=`identify -format "%wx%h" $i`;
w=`echo $aux | cut -f1 -d"x"`;
h=`echo $aux | cut -f2 -d"x"`;
resize=`echo ${h}x`;

if (( $w < $h )); then resize=`echo "x$w"`; fi

#echo "$i: ${w}x$h -> $resize";

eval $EXEC $i $EXECOPT $resize `echo $i | sed s/.jpg/sca.jpg/`;
done

Espero que esta entrada os sirva de ayuda y podáis reducir esas cientos de imágenes de vuestras vacaciones.

Un saludo,
Morpheus

25 julio 2011

Iniciando sesión como root sin GDM

Aprovecho que me he puesto a escribir para mostrar un pequeño truco para iniciar sesión como usuario root con interfaz gráfica sin usar GDM.

Para ello, primero iniciamos sesión en una terminal virtual (sin X), ya que en la terminal que ofrece GNOME dbus da problemas; para acceder a ella, pulsamos Ctrl + Alt + Fn, siendo n un número del 1 al 6. Una vez iniciada la sesión, ejecutamos el siguiente comando, siendo m un número de visor (display), normalmente mayor que 0: Xorg :m & gnome-session --display :m

Este comando inicia una instancia del servidor Xorg en el display :m y lo deja ejecutándose en segundo plano; tras ello, inicia una sesión de gnome en el display :m.

Una vez cerrada la sesión de GNOME, el servidor Xorg seguirá funcionando en la terminal virtual que dejamos abierta; si queremos cerrarlo, podemos ejecutar fg
(Ahora pulsamos el habitual Ctrl+c para cerrar el programa que se encuentra en primer plano)
o bien kill %nsiendo n el número de trabajo de esa terminal; puedes ver los trabajos ejecutados con el comando jobs.

Un saludo,
Morpheus

Hibernando en Debian

Desde hace algún tiempo he estado tratando de asimilarme a Debian, pues hay varias cosas que hace tiempo que me hastían de Ubuntu, pese a que aún no he encontrado una distribución con unos repositorios tan completos sin tener que añadir externos; es por eso que últimamente ando usando Linux Mint en vez de Ubuntu.

Sin embargo, decidí actualizar de Linux Mint 10 a 11 haciendo un cambio de repositorios (cosas que desaconsejaban desde Linux Mint, pero es que quería intentar ahorrarme la configuración e instalación de programas una vez reinstalado el sistema operativo), lo cuál me ha llevado a tener un problema con dependencias y demás con el propio kernel.

Por suerte, contaba con una instalación de Linux Mint Debian Edition (LMDE), con lo cuál he aprovechado para pasar un par de días instalado en él, a ver si consigo no echar a faltar demasiado los repositorios de Ubuntu, desde Linux Mint, pues usa los repositorios d Ubuntu como base.

Una vez actualizado, he recordado uno de los escollos que tenía con Debian (además de unos cuantos detalles que en Ubuntu te los dejan hechos), y era la hibernación: si quiero reiniciar para pasar un rato jugando en Windows, me gusta poder hacerlo sin preocuparme de lo que dejo a medias; sin embargo, en Debian no conseguía que funcionase correctamente. Tras analizar algunos logs y mensajes del kernel y buscar en diversas fuentes, he encontrado la manera de arreglarlo.

Para poneros en contexto sobre mi situación, diré que tras analizar los registros de /var/log/pm-powersave.log y /var/log/pm-suspend.log, y leer el siguiente mensaje durante el inicio del sistema (boot): Invalidating stale software suspend images, me dí cuenta de que el problema estaba en la restauración; es decir, que el sistema hibernaba bien (guardaba la imagen en disco), pero al iniciar, no la restauraba.

Para solucionar este problema, basta con añadir en la línea de kernel de grub del archivo /boot/grub/grub.cfg (en mi caso, grub2) la opción resume:

linux /boot/vmlinuz-2.6.39-2-amd64 root=UUID=root ro resume=UUID=swap quiet

Donde root es el UUID de la partición del sistema y swap es el UUID de la partición de swap.

Cabe decir que, si se actualiza el grub, el archivo volverá a cambiar y la opción de restaurar desaparecerá; para evitar esto, la opción de resum podéis añadirla al archivo /etc/default/grub, en la variable GRUB_CMDLINE_LINUX:

GRUB_CMDLINE_LINUX="resume=UUID=swap"

Una vez hecho esto último, ejecutáis como usuario root:

# update-grub

A partir de ahora, cada vez que hibernes, Debian restaurará la imagen del sistema, dejando el pc como cuando lo hibernaste.

Espero que a alguien le sirva de ayuda, al igual que a mí.

Actualización 16 de marzo de 2012: Como método alternativo al del grub, podéis poner la misma línea en /etc/initramfs-tools/conf.d/resume; tened en cuenta las mayúsculas:

RESUME=UUID=swap

Después de esto, ejecutáis el siguiente comando como root:

# update-initramfs -u

Un saludo,
Morpheus

01 julio 2011

Algunos avisos para las nuevas versiones de Firefox

Dado que voy con los repositorios de Lignux, a penas he notado las mejoras y cambios del nuevo Firefox (de hecho, ahora mismo sigo desde un firefox 3.6.18); sin embargo, he estado trasteando un poco con Firefox 4 y 5 y ha habido ciertos cambios en lo que se podía configurar desde Opciones.

Una de ellas es el antiguo aviso para guardar las pestañas. Para recuperarlo, tienes que entrar en about:config, prometer que vas a tener cuidado, y poner la siguiente condición a cierto (puedes utilizar el filtro de opciones para encontrarla más rápidamente): browser.showQuitWarning

Actulización: Cabe decir que se puede configurar en opciones la posibilidad de que Firefox recuerde las pestañas y ventanas de la última sesión; sin embargo, creo que recordar que en la versión 4 había problemas con esta opción y no acababa de funcionar correctamente. En fin, lo importante es que se pueda configurar Firefox de la manera que le vaya mejor a cada uno.

Otro de los avisos que ahora ya no se pude configurar, al menos con Firefox 5, es todos aquellos avisos relacionados con la seguridad y el envío de información no cifrada; en mi opinión, es un aviso que va muy bien si andas en alguna red wifi. En concreto, el aviso que propongo es el de que se va a proceder a enviar información por un canal no cifrado; para ello, ponemos a true el siguiente parámetro en about:config: security.warn_submit_insecure

Un saludo,
Morpheus

30 marzo 2010

Leer entrada estándar (STDIN) en Java

Por motivos académicos, tengo que aprender a programar en Java; así que, tras haber aprendido C/C++ y al ser Java un lenguaje mucho más orientado a objetos que C++, quería practicar un poquito haciendo la traducción de algunos programas que he escrito en C++ a Java. Para ello necesito leer datos de la entrada estándar; pero tras buscar referencias, tanto en la documentación de Java como en diferentes blogs y paginas, no he conseguido encontrar una manera más o menos sencilla de hacerlo.

De todas maneras, tras leer un poco más a fondo algunas referencias y la documentación, he conseguido hacerlo de una manera algo más sencilla, imitando el comportamiento del while(cin >> var) de C++:

C++ Java
#include <iostream>
using namespace std;

int main(int argc, char **argv) {
  int n;
  while (cin >> n) {
    /* Código */
  }
}
import java.util.Scanner;

class main {
  static public void main(String[] args) {

    Scanner scanf = new Scanner(System.in);

    while (scanf.hasNextInt()) {
      int n = scanf.nextInt();
      /* Código */
    }
  }
}

Aquí tenéis la documentación del elemento Scanner.

Espero que este post os haya servido de ayuda.

Actualización 31/03/2010: Por desgracia, la solución supuestamente sencilla no sirve para leer char; para ello tenéis que hacer esto:

try { char c = (char) System.in.read(); }
catch (Exception e) {}

Si alguien conoce otra manera más sencilla y menos chapucera que lo que he escrito en este post, que lo comente.

Actualización 2: Si los char van a estar separados por espacios o saltos de línea, puedes hacer una de estas dos:

// scanf.next() devuelve un String
char c = scanf.next().charAt(0);
char c = scanf.next().toCharArray()[0]; // Ambas son equivalentes

Un saludo,
Morpheus

HP PSC 2175 en Windows desde GNU/Linux

Desde hace aproximadamente un año llevo utilizando mi impresora conectada en GNU/Linux desde Windows, y creo que es algo que algunos aún querrán conseguir. Así pues, he escrito este tutorial para mostrar la chapuza que he hecho para conseguirlo.

Esto sólo lo he probado en WinXP, pero supongo que es probable que también sirva en WinVista o Win7; ha sido probado desde Debian Squeeze y Ubuntu Jaunty. La impresora en cuestión es una HP PSC 2175 all-in-one.

Una vez dados los datos, vamos allá:

Lo primero que tienes que hacer es asegurarte de que tienes la siguiente configuración en tu archivo /etc/samba/smb.conf:

[print$]
comment = Printer Drivers
path = /var/lib/samba/printers
browseable = yes
read only = yes
guest ok = no

Recuerda que tu equipo tiene que estar en el mismo grupo de trabajo que los ordenadores con los que quieres compartir tu impresora (por defecto Workgroup). Si no es así, puedes cambiar la red con la aplicación shares-admin (esta aplicación no se suele encontrar en los menús de Gnome, tendrás que ejecutar el comando desde una consola) o bien editando en el mismo smb.conf:

# Change this to the workgroup/NT-domain name your Samba server will part of
workgroup = GrupoDeTrabajo

Ahora tienes que compartir la impresora conectada a tu Lignux. Para ello, utiliza la aplicación system-config-printer, que podéis encontrar en Sistema->Administración->Impresoras:

Configuración de la impresora como compartida
Selecciona la Impresora y edítala, dejándola como compartida

Configuración del servidor para publicar Impresoras compartidas
Configura el servidor para que publique las impresoras compartidas en la red (Archivo->Opciones del Servidor)

Una vez hecho esto, le toca el turno a Windows.

Si todo está bien, deberíamos ver nuestro equipo de trabajo en Windows, y este debería contener la impresora como recurso compartido (además de los que tuvieras previamente):

Vista de recursos compartidos del equipo en la red local de Windows

Clicamos dos veces sobre la impresora a la que queremos conectarnos (en mi caso Impresora) y nos dará dos mensajes de alerta; el último nos informará de que no se encuentran los controladores instalados, y nos ofrecerá buscarlos en su lista; aceptamos, llegando al siguiente menú:

Menú de selección de controladores

Como podéis ver, nuestra impresora no se encuentra en esa lista por mucho que hayamos instalado los drivers, así que intentamos encontrar una impresora lo más parecido a la nuestra, en mi caso HP PSC, aunque esto no es algo relevante porque más adelante redefiniremos los controladores.

Una vez instalada, necesitaremos los controladores que vamos a utilizar para conectarnos a la impresora de Lignux. Para ello necesitaremos el programa PDF Creator, que podéis descargar de la página pdfforge.org. Lo descargáis e instaláis. Este programa nos instalará una impresora PDF con un nombre por defecto de "PDF Creator", que además de ser muy útil para previsualizar los documentos antes de imprimirlos y juntar archivos pdf, nos servirá como controlador para nuestra impresora.

Ahora vamos a "Impresoras y faxes" y seleccionamos la impresora que hemos agregado:

Impresoras y Faxes, del Panel de Control de Windows

Nos aparecerá la ventana de gestión de impresión. Abrimos el menú de Propiedades, en Impresora->Propiedades, y en la pestaña "Opciones Avanzadas" seleccionamos como controlador PDF Creator:

Propiedades de Impresora, opciones avanzadas

Le damos a aceptar y listo.

Tras esto, la impresora debería imprimir correctamente cualquier archivo; así lo hace en mi caso.

Espero que este tutorial os haya servido de ayuda, o bien que os sirva de base para encontrar la solución.

Un saludo,
Morpheus

16 agosto 2009

Conversión de texto a ASCII y hexadecimal y cifrado con MD5 y SHA-1 en GNU/Linux

Buenas!

Alguna vez, por simple curiosidad o por trastear alguna base de datos de algún programa que haya instalado en mi ordenador (como por ejemplo Wordpress), he necesitado convertir un texto de ASCII a hexadecimal, o viceversa, o quizás haya querido cifrar (uso cifrar, por ser correcto y no levantar muchas ampollas, pero creo que, a mi parecer, no hay motivos para no usar encriptar teniendo en cuenta la palabra criptografía) algún tipo de texto.

Es por esto que he escrito este mini manual para hacer esas tareas y para tenerlo como apunte, sobre todo la transformación de hexadecimal a ASCII, que por alguna extraña razón me ha costado dar con ella, más concretamente dar con el comando adecuado para ello.

Espero que este post os sea útil.

Conversión a ASCII y hexadecimal

Para convertir un texto en ASCII a hexadecimal (lo que el ordenador realmente almacena en su memoria), podemos utilizar el comando xxd (que viene incluido con el editor de textos vim):

$ xxd
hola
0000000: 686f 6c61 0a hola.

Para que acabe el comando y nos escriba el resultado tenemos que pulsar Ctrl+D.

Si queremos que nos escriba (o lea) el resultado sin esa estructura, podemos utilizar el argumento -p:

$ xxd -p
hola
686f6c610a

Con el uso de este argumento, el programa ya nos escribe el resultado al pulsar Entrar o Enter, y usamos Ctrl+D para terminar el comando (o también podemos usar Ctrl+C, que interrumpe y para el comando).

Ahora, si lo que queremos es transformar un texto en hexadecimal a ASCII, utilizaremos el mismo comando con el argumento -r del mismo modo:

$ xxd -r
0000000: 686f 6c61 0a hola.
hola

$ xxd -r -p
686f6c610a
hola

En estos últimos 2 casos podéis utilizar el último procedimiento que hemos explicado antes de ellos para terminar el comando.

Y si queréis redireccionarlos a un archivo de salida:

$ xxd -p > hola.hex
hola

o utilizar un archivo de entrada:

$ xxd -r -p < hola.hex
hola

Si queréis hacer cosas parecidas pero con una interfaz gráfica, podéis usar Ghex, un editor hexadecimal para GNOME.

Cifrado mediante MD5 y SHA-1

Ahora pasemos al cifrado de texto con MD5, un algoritmo bastante conocido de hash (identificador único de un archivo o texto). Para ello, utilizaremos el comando md5sum, y el argumento -, que le indica al comando que queremos que nos cifre lo que le vamos a escribir.

$ md5sum -
hola4d186321c1a7f0f354b297e8914ab240 -

En este caso, nos ha hecho falta pulsar 2 veces Ctrl+D.

Tengo que hacer notar que es muy importante escribir exactamente lo que queremos que nos cifre, sin espacios ni saltos de línea de más, ya que nos dará resultados muy diferentes:

$ md5sum -
hola 51db41a195b9a4b26f9e1f16d72e7b20 -

$ md5sum -
hola
916f4c31aaa35d6b867dae9a7f54270d -

En este último caso, en el que pulsáis Entrar, sólo teneís que pulsar una vez Ctrl+D.

Cómo podéis observar, la cosa cambia enormemente, pero el resultado no queda claro, ya que se entremezcla con los datos que hemos introducido. En este caso, pues, lo mejor es redireccionar el resultado a un archivo:

$ md5sum - > salida.txt
hola

Observamos que el resultado es el mismo:

$ more salida.txt
4d186321c1a7f0f354b297e8914ab240 -

Podemos, como ya hemos hecho anteriormente, redireccionar la entrada al comando. Sin embargo, tenemos que recordar la importancia de que el texto introducido en el archivo sea exactamente el mismo que queremos cifrar, teniendo en cuenta que algunos editores de texto, como Gedit, incluyen por defecto un salto de línea final en el documento. Por tanto debemos estar muy seguros de que el archivo que utilizamos no tenga espacios ni saltos de línea de más.

$ md5sum < hola.txt
4d186321c1a7f0f354b297e8914ab240 -

En caso de que queramos cifrar el texto mediante el algoritmo SHA1, sólo tenemos que utilizar el comando sha1sum y proceder de la misma manera que con md5sum:

$ sha1sum < hola.txt
99800b85d3383e3a2fb45eb7d0066a4879a9dad0 -

Ambos comandos de cifrado son parte del proyecto GNU.

Un saludo,
Morpheus

03 marzo 2009

Compilar código de 32 bits en 64 bits con gcc

Si por alguna razón tenéis que compilar un código como si estuvierais en una máquina de 32 bits, en vez de una de 64, podéis hacerlo siguiendo estos pasos:

1) Instalamos el paquete gcc-multilib (para lenguaje C):

sudo aptitude install gcc-multilib

2) Compilamos usando la opción del compilador -m32:

gcc foo.c -o foo.x -m32

Este tutorial funciona en Ubuntu, aunque supongo que funciona en cualquier distribución que cuente con el paquete gcc-multilib (o equivalente).

Si lo que queréis compilar es C++, en vez de C, el paquete a instalar es g++-multilib.

Esto es útil si os pasa como a mí, que necesitaba compilar un programa pero no contaba con el código objeto compilado para 64 bits, sólo para 32. Con esto, podréis compilarlo todo como 32 bits sin ningún problema.

Un saludo,
Morpheus

09 diciembre 2008

Instalar GNU/Linux en un USB

Buenas!

Hace aproximadamente un mes, conseguí instalar GNU/Linux en una memoria USB, algo que para muchos puede resultar útil, así que me he dispuesto a hacer un pequeño tutorial en el que explico cómo hacerlo, tanto en Debain (Etch), como en Ubuntu (Intrepid Ibex), aunque esencialmente es lo mismo.

Para instalarlo, vamos a proceder de la siguiente manera: primero vamos a instalar el sistema base (los programas de consola básicos para utilizar el ordenador e instalar más programas) y posteriormente vamos a instalar sólo los programas que necesitemos (ya que supongo que en una memoria USB no tendréis mucho espacio; yo tengo 2 GB y Ubuntu ocupa 1'6 GB, con navegador, lector de correo, mensajería instantánea, editor de particiones, procesador de textos, de hojas de cálculo, etc.) mediante aptitude o apt.

Será necesario que la memoria USB sea de almenos 1'5 GB.

Instalar sistema base con Ubuntu


Lo primero que necesitamos es la versión Alternate CD de Ubuntu. Con ella, podremos acceder a opciones avanzadas en la instalación, como la opción de poder instalar el sistema base, el precio que hay que pagar es una instalación en modo texto, aunque no os preocupéis, la instalación es muy intuitiva.

Bien, una vez que tengáis en vuestro poder el Alternate CD (y el USB donde vais a instalar el sistema), lo que tenéis que hacer es iniciar con el CD, y cuando aparezca las opciones de arranque, seleccionar "Instalación de sistema de consola", en el menú de "Modos", al que podéis acceder pulsando F4. Ahora simplemente tenéis que pulsar "Instalar" y comenzará el asistente de instalación.

El asistente de instalación es bastante intuitivo, y sólo tenéis que ir siguiendo los pasos que tiene, e ir contestando a sus preguntas. El único punto delicado es la definición de particiones, donde debéis indicar que el USB es el punto de montaje raíz (/), donde se instalará el sistema.

Una vez instalado el sistema, sólo tenéis que extraer el CD, iniciar Ubuntu desde el USB y disponeros a instalar el entorno de escritorio y programas que queráis mediante la consola (sistema base) instalado en él.

Instalar sistema base con Debian


Instalar un sistema de consola con Debian es ligeramente más sencillo que con Ubuntu, así que no os asustéis. Además, no hace falta que instaléis el sistema en modo texto, sino que podéis instalarlo con un sistema gráfico (que dice lo mismo que el modo texto, pero más bonito y, aparentemente, más intuitivo) mediante la opción installgui de boot.

Quizá me haya ido un poco por las ramas, así que vayamos por partes, y en orden:

Lo primero que tenéis que hacer es descargar el CD de Debian que se adapte a vuestra arquitectura de procesador (si tenéis dudas de que clase de microprocesador tenéis, descargad la versión i386), y el CD 1 (el entorno de escritorio por defecto es GNOME, pero podéis seleccionar KDE o XFCE, que también aparecen como CD 1).

Cuando tengáis el CD, cuando este inicie, podéis simplemente pulsar "Entrar" o bien teclear installgui e instalaréis Debian con un entorno gráfico.

Sea lo que sea, modo texto o gráfico, sólo tenéis que seguir los pasos hasta la etapa de particionado del disco, que como ya he explicado con Ubuntu, tenéis que seleccionar como sistema raíz de ficheros el USB; y la etapa de "Selección de programas", en la cual lo único que tenéis que seleccionar es "Sistema estándar", aunque si estáis muy seguros de tener espacio, podéis también seleccionar "entorno de escritorio" y saltaros el paso de instalar el entorno mediante apt o aptitude.

Una vez hecho esto, sólo tenéis que iniciar desde el USB, y pasar a instalar el entorno de escritorio y los programas que necesitéis.

Instalar el entorno de escritorio y el resto de aplicaciones mediante aptitude


Lo primero que tenéis que hacer, es iniciar sesión en la consola. Una vez iniciada, tenéis que seguir los siguientes pasos:

Si te encuentras en Debian, primero tienes que estar en modo root (con el comando su), y si te encuentras en Ubuntu, tienes que incluir primero el comando sudo delante de los comandos siguientes:
aptitude install xorg gnome-core gdm synaptic network-manager
Con este comando, lo que hemos hecho ha sido instalar el sistema base: xorg para el entorno gráfico, gdm para entrar/iniciar sesión en xorg, gnome-core para instalar lo básico para hacer funcionar el entorno de escritorio GNOME, synaptic para instalar programas en modo gráfico (incluido por gnome-core en Ubuntu) y network-manager para poder conectarnos a internet más fácilmente.

Ahora, si queremos hacer el sistema algo más funcional (con el mínimo espacio, ya que yo no contaba con él) y práctico, podemos ejecutar este comando:
aptitude install abiword gnumeric vlc gthumb thunderbird firefox flashplugin-nonfree pidgin gnome-system-tools evince unrar file-roller
Lo que nos provee de un cliente de IM (pidgin), un reproductor multimedia (vlc), un procesador de textos (abiword), hoja de cálculo (gnumeric), navegador (firefox), correo electrónico (thunderbird), gestor de usuarios (gnome-system-tools), gestor de fotografías (gthumb, que permite ver gif's animados), lector de PDF (evince), una utilidad para descomprimir y comprimir archivos (file-roller, con unrar para poder leer archivos rar) y el flash de Adobe (flashplugin-nonfree) para ver los contenidos flash de las páginas web (no disponible en Debian, usar swf-player en su lugar o instalarlo manualmente).

Si además queréis tener el sistema traducido al castellano (español de España), tenéis que ejecutar este comando:
aptitude install language-pack-gnome-es language-pack-es wspanish thunderbird-locale-es-es myspell-es
Después de todo esto, tendréis GNU/Linux en vuestra memoria USB, que os podrá ser de ayuda en algunas situaciones. Yo, por ejemplo, tengo instalado, además de los programas que os he incluido en este tutorial, un editor de particiones (gparted), un antivirus (clamtk), un gestor de unidades (gnome-device-manager) y un compilador de C++ (g++), entre otros, que me permiten reparar un PC dañado, así como poder programar en cualquier lugar con un ordenador (motivos académicos n.n¡).

Sólo me falta decir que espero que este tutorial os haya sido útil.

Un saludo,
Morpheus

12 agosto 2008

Restaurar Usplash

Si utilizáis StartupManager para gestionar vuestro GRUB y habéis instalado un usplash personalizado, pero resulta que por alguna extraña razón el usplash ha dejado de aparecer y se ven los mensajes del SO, comprobad que la resolución de pantalla que habéis configurado en el StartupManager sea la misma que hay en el archivo /etc/usplash.conf.

Si no es la misma, cambiadla (y ponedle la resolución correcta) y una vez hecho esto, escribid lo siguiente en una terminal:
sudo dpkg-reconfigure usplash
Y ya volveréis a tener vuestro usplash como siempre en el próximo reinicio.

PD: Si queréis comprobar si el Usplash se verá bien, podéis ejecutar el siguiente comando en una consola:
sudo usplash -c
Actualización 18/08/2008: Si el usplash os empieza a cargar pero a los pocos segundos os empiezan a aparecer los mensajes de arranque del SO, probablemente tengáis un problema con la swap (a mí me pasó después de probar a instalar Debian, 2 veces XD). Para arreglarlo, podéis hacer lo siguiente:
  1. Abrir Gparted (el editor de particiones de Gnome, aunque podéis utilizar otro, claro está) y reformatear vuestra partición de swap, que probablemente no estará montada/activada.

  2. Teclear en una terminal el siguiente código:
    sudo blkid

  3. Buscar y copiar la UUID de la partición de swap

  4. Buscar la entrada para la partición de swap en el archivo /etc/fstab y pegar (probablemente sustituir) la UUID

  5. Teclear en una terminal lo siguiente:
    sudo update-initramfs -u

Una vez hecho esto, el usplash debería funcionar en el próxima arranque de sistema.

Fuente: Ubuntuforums.org (1 y 2)

Un saludo,
Morpheus

24 julio 2008

MIDI y otros formatos de audio y vídeo en Ubuntu GNU/Linux

Para poder reproducir MIDI (tanto con Totem, como con Rhythmbox), sólo tenéis que instalar el paquete freepats.

Para reproducir otros archivos tendremos que instalar los siguientes paquetes:

Pudiera ser que me haya equivocado especificando el tipo de formato que es capaz de abrir cada paquete, pero con estos 3 tendréis las espaldas más que cubiertas para casi cualquier archivo multimedia.

Un saludo,
Morpheus

Instalando un servidor LAMP en Ubuntu (2): Wordpress, mod_rewrite y sendmail

Disclaimer: Sé que este post tiene un desfase de un año respecto al primero (y sé que en el primero dije que en breve tendríais este post), pero tuve problemas con el envío de correos y no quise postear nada hasta que lo tuviera todo comprobado, pero un día, misteriosamente, mi ordenador dejó de enviar correos, así que decidí esperar a conseguir solucionar el problema (algo que nunca llegué a hacer, antes cambié de router, y parece que ya funciona, supongo que el problema sería eso). De todos modos, doy mis disculpas por tan tremendo retraso en un post. Bueno, al menos más vale tarde que nunca. :S

Siguiendo con el post sobre cómo instalar y preparar (de forma básica) un servidor LAMP, os voy a explicar cómo configurar el módulo rewrite (para que funcione) y qué necesitáis para enviar correos electrónicos desde vuestro localhost. Añadir que lo que os voy a explicar seguramente os sirva para algunas funcionalidades de Wordpress y seguramente para otros tipos de CMS, aunque de momento no he probado ningún otro.

Bien, lo primero que vamos a hacer es activar el módulo rewrite (ya viene incluido en la instalación de apache2):

sudo a2enmod rewrite

Ya tenemos el módulo activado. Ahora, tenemos que añadir lo siguiente al archivo /etc/apache2/httpd.conf:
<Directory "/var/www/wordpress/">
Options FollowSymLinks
AllowOverride All
</Directory>

Una vez hecho esto, ya tenemos preparado el módulo rewrite. Pasemos al envío de correos electrónicos desde tu localhost.

Para hacerlo, sólo tienes que instalar sendmail o bien postfix. He probado a usar sendmail sin el puerto SMTP abierto (el puerto 25) y me ha enviado el correo, así que no es necesario que esté abierto.

Un saludo,
Morpheus