Archivo

Archivo para la categoría ‘Linux’

Iniciación a los memory leaks [ejemplos en C++]

Lunes, 28 de Noviembre de 2011 Gaspar Fernández Sin comentarios

leak

Hablemos de un fenómeno que nos afecta, sobre todo en desarrollos que están en ejecución durante mucho tiempo, pero , y es que, debido a la mala gestión de la memoria podemos llegar a consumir más de lo necesario y podemos volver loco al sistema operativo utilizando la memoria virtual para darnos el espacio que necesitamos.

Los memory leaks son fugas de memoria debidas a que hemos pedido un cierto espacio de memoria durante la ejecución de nuestro programa, la hemos usado y cuando hemos dejado de usarla, no la hemos liberado, por tanto estamos ocupando más de lo que necesitamos y acaparando recursos; un ejemplo de esto son esos procesos o programas que tenemos arrancados durante varios días, y cuanto más tiempo llevan arrancados más memoria ocupan (a veces es necesario, pero otras veces, la mayoría, no).

Antes de iniciarnos en la creación de un programa acerca de lo que no debemos hacer, vamos a hacerlo bien, y echaremos un vistazo a un artículo sobre el Out Of Memory Killer (desconozco si Windows tiene algo parecido, tal vez instalando alguna aplicación), que será una utilidad de nuestro kernel que matará el proceso cuando esté consumiendo mucha memoria, o nos permitirá matarlo fácilmente con las teclas (Alt+SysRq o imprimir pantalla+F). Con unas líneas antes de nuestro programa, nos podemos evitar un disgusto, como puede ser un reinicio de sistema mientras hacemos pruebas, y puede que estemos ejecutando algo útil.

¡Precaución!

Si vemos que nuestro proceso hace que el sistema vaya muy lento, será porque en lugar de utilizar nuestra RAM, estará usando la memoria virtual de forma intensa, esta memoria es mucho más lenta que nuestra RAM, si hemos entendido lo que quiero demostrar, podemos parar el proceso gracias al OOM Killer de antes con Alt+SysRq+F, pero sólo pulsa una vez las teclas, aunque veamos que no nos hace mucho caso, el sistema tarda un tiempo en volver a recolocar toda la información y limpiar la memoria del proceso. Si pulsamos más veces, lo mismo terminamos matando el servidor gráfico o algún proceso importante, y no queremos eso.

Ajustando OOM Killer

Para todo eso, debemos incluir el siguiente código (al final pondré todo el programa, pero primero lo analizamos por separado), el objetivo es escribir el valor 15 sobre el fichero /proc/PID/oom_adj:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void error(const char *mens)
{
  cerr << "ERROR: ("<<mens<<") errno: "<<errno<<": "<<strerror(errno)<<endl;
  exit (1);
}

void ajusta_oom()
{
  ofstream fi;
  char nom[30];

  // Obtenemos el nombre del fichero, aunque estemos haciéndolo
  // a lo C++, uso sprintf() aquí porque es más rápido.
  sprintf(nom, "/proc/%d/oom_adj", getpid());

  // Abrimos el fichero
  fi.open(nom);
  if (fi.fail())
    error("No puedo abrir oom_adj");

  // Escribimos 15 en el fichero, para dar más "apeletas de muerte"
  // a este proceso. Ver:
  // http://totaki.com/poesiabinaria/2010/04/cuando-un-proceso-se-come-la-memoria-de-nuestro-sistema/
  fi<<"15";
  if (fi.fail())
    error("No puedo escribir oom_adj");

  fi.close();
}

Gastando memoria

Para esta parte hay que tener cuidado, ya que depende de la RAM que tengamos cada uno, yo he hecho esto en un equipo con 2GB de RAM, hasta que se ha vuelto extremadamente lento. Aunque el objetivo es que veamos la memoria que está actualmente en consumo. Podemos utilizar el comando free() en paralelo con el programa para ver que cada vez tenemos más memoria consumida, cuando no deberíamos, porque la intuición nos diría que cuando salimos de la función todo lo reservado se libera, pero no es así.
En los sistemas operativos actuales no podremos ponernos a reservar memoria sin control, ya que el SO casi siempre nos la dará (aunque no disponga de ella, dejando el marrón al gestor de memoria del futuro), si de verdad queremos reservar memoria, gastarla y darnos cuenta, debemos utilizar la memoria que reservamos; por ello he optado por rellenar un vector de números. Cuidado con subir mucho más MAX_NEW_ELEMENTS, es más, si tienes poca memoria, te recomiendo bajarla un poco, a la mitad, por ejemplo, en un primer cálculo, sólo contando los elementos del vector (8.000.000 x 8 (long int) = 64Mb), por lo que depende de lo que haya en ejecución, con cuatro llamadas a la función gasta_memoria() habremos gastado 256Mb de memoria.
Para ver lo que vamos gastando, debemos ejecutar en un terminal el programa que voy a mostrar, y en otro terminal ejecutamos free. El programa hará varias paradas pidiendo que pulsemos intro a cada llamada de gasta_memoria() para que podamos ver lo que se ha gastado, después del código veremos la ejecución.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <iostream>
#include <vector>
#include <cstdio>
#include <unistd.h>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <fstream>

#define MAX_NEW_ELEMENTS 8000000

using namespace std;

// Sale del programa devolviendo un error
void error(const char *mens);
// Ajunta oom_adjust para matar este proceso si se nos cuelga el sistema
// Así nos quitamos un disgusto
void ajusta_oom();
// Gasta memoria (casi) sin control
void gasta_memoria();
// Espera una tecla
void intro();

void gasta_memoria2()
{

  vector <long int> *v;


  v=new vector<long int>;

  for (unsigned long i=0; i<MAX_NEW_ELEMENTS; i++)
    v->push_back(i);

  intro();
  // delete v;
}


int main()
{
  ajusta_oom();
  cout << "OOM ajustado" << endl;
  intro();
  gasta_memoria();
  gasta_memoria2();
  gasta_memoria();
  gasta_memoria2();
}

void intro()
{
  char key;
  cout << "Pulse Intro"<<endl;
  cin.get(key);
}

void gasta_memoria()
{
  vector <long int> *v;

  v=new vector<long int>;

  for (unsigned long i=0; i<MAX_NEW_ELEMENTS; i++)
    v->push_back(i);

  intro();
  // delete v;
}

void error(const char *mens)
{
  cerr << "ERROR: ("<<mens<<") errno: "<<errno<<": "<<strerror(errno)<<endl;
  exit (1);
}

void ajusta_oom()
{
  ofstream fi;
  char nom[30];

  // Obtenemos el nombre del fichero, aunque estemos haciéndolo
  // a lo C++, uso sprintf() aquí porque es más rápido.
  sprintf(nom, "/proc/%d/oom_adj", getpid());

  // Abrimos el fichero
  fi.open(nom);
  if (fi.fail())
    error("No puedo abrir oom_adj");

  // Escribimos 15 en el fichero, para dar más "apeletas de muerte"
  // a este proceso. Ver:
  // http://totaki.com/poesiabinaria/2010/04/cuando-un-proceso-se-come-la-memoria-de-nuestro-sistema/
  fi<<"15";
  if (fi.fail())
    error("No puedo escribir oom_adj");

  fi.close();
}

La ejecución será escalonada, es decir, ejecutamos, y antes de pulsar intro ejecutamos free(), así vemos cómo se va gastando la memoria tal y como indica la imagen.
testi
Lo que quiero demostrar es que el puntero al vector v, cada vez que ejecutamos new vector sobre ella, restablece su valor, y por tanto, aunque la memoria asociada a ese vector sigue reservada (y encima tiene 8000000 de elementos), ya no tenemos forma de acceder a ella, ya no sabemos la dirección de memoria con la que accedemos; vamos que el hecho de que no sea accesible no significa que no esté reservada, en uso y molestando, porque el sistema no podrá utilizar esos datos para otras cosas.
En este caso, la solución a este problema habría sido escribir:

1
delete v;

al final de la función gasta_memoria(). Aunque en ocasiones esto no es tan fácil de ver.

Cuando el programa se cierra…

Generalmente el sistema operativo, que sí que sabe lo que el proceso ha reservado y consumido, libera la memoria, y a veces puede invertir un rato, aunque a veces no aparezca toda la memoria como libre directamente, al cabo de unos minutos (como mucho) ya estará listo.

Si el sistema no reacciona…

Recuerdo, que si hemos reservado mucha, mucha memoria, el sistema dejará de responder, para ello podemos pulsar Alt+SysRq+F, y el programa morirá rápidamente (pero, pulsad estas teclas, sólo una vez, y esperar).

Foto: tao_zhyn (Flickr) Compartido con CC-by a 26/11/2011

Conociendo el proceso que me ha enviado una señal (signal)

Jueves, 24 de Noviembre de 2011 Gaspar Fernández Sin comentarios

Hace tiempo hablábamos de capturar señales, aunque en ocasiones, es necesario saber quién me envía esa señal, si por ejemplo nos envían un SIGINT o SIGTERM, tal vez queremos saber qué proceso nos quiere muerto y qué usuario lo ha invocado. O tal vez estamos esperando una señal de control (SIGUSR1, por ejemplo) por parte de un proceso cliente específico.

El problema es que por nuestro modo actual de direccionar señales (con signal(señal, funcion)) sólo comunicamos el número de señal que se ha recibido, sin más información.

signals_from

Para capturar esta información necesitamos utilizar sigaction para establecer la función a la que llamaremos cuando llegue la señal, para utilizarla podemos utilizar el siguiente código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

void recibe_signal(int signum, siginfo_t *siginfo, void *context)
{
  /* Mostramos toda la información necesaria */
  printf ("[%d] Recibida %d (%s) de %d (UID: %d) ¿Error? %d \n",
      getpid(),
      signum,
      strsignal(signum),
      siginfo->si_pid,
      siginfo->si_uid,
      siginfo->si_errno);
}


int main(int argc, char *argv[])
{
    struct sigaction sact;
   
    sigfillset(&sact.sa_mask);
    /* Definimos la función callback */
    sact.sa_sigaction = recibe_signal;
    /* Queremos usar el callback sigaction en lugar del dado por signal() */
    sact.sa_flags     = SA_SIGINFO;

    sigaction(SIGUSR1, &sact, NULL);

    /* Bucle infinito, para cerrar el proceso, utilizar kill -9 PID */
    while (1)
      pause;

    return 0;
}

Como vemos sigaction tiene 3 parámetros:

  • La señal que queremos capturar
  • Un registro de tipo struct sigaction
  • La acción que se ejecutaba antes, por si queremos guardarla para restaurarla más tarde

Dentro del registro, tendremos como valores importantes (struct sigaction act):

  • sact.sa_sigaction : que especifica la función que la llamaremos, esta función será del tipo void funcion(int, siginfo_t*, void*), donde el primer int recibirá la señal que nos han enviado (tal y como hacía la función que asignábamos con signal), el segundo parámetro será información asociada a la señal, y la tercera el contexto de ejecución.
  • sact.sa_handler : será la función a la que se llama, es la misma que cuando hacemos signal(señal, funcion)
  • sact.sa_flags : Es un conjunto de flags, si especificamos SA_SIGINFO, estaremos dando paso a la función especificada por sa_sigaction, si no utilizaremos sa_handler. Aunque esta variable vale para más cosas, para esta práctica nos vale con saber esto. (man sigaction para más info)
  • sact.sa_mask
  • : Nos proporciona una máscara de las señales que serán bloquedas, podemos utilizar sigfillset() para rellenar toda la máscara automáticamente. Si queremos que unas se bloqueen y otras no podemos utilizar sigaddset() y sigdelset()

Ahora bien, la función que se llamará cuando venga una señal tendrá el prototipo que hemos visto antes con tres parámetros: la señal recibida, la información de la señal con un puntero a siginfo_t y el contexto de ejecución, que se pasará con un puntero a void, que por ahora no utilizaremos. La estructura que controla la información de la señal entre otros muchos datos nos facilitará lo siguiente:

  • si_pid : PID que nos envía la señal (y esta acción da título al post)
  • si_uid : Usuario que ejecuta el proceso que nos manda la señal
  • si_errno : Algún error que haya causado la señal
  • … para más información, man sigaction

Para preparar la sentencia tendremos que escribir varias líneas definiendo los valores del registro así como la llamada a la función sigaction(), por lo tanto podremos crear una función que tenga los parámetros más comunes a la hora de definir este tipo de acciones, así lo llamamos como a signal():

1
2
3
4
5
6
7
8
9
10
11
12
int sigact(int signum, void funcion(int, siginfo_t *, void *))
{
    struct sigaction sact;
   
    sigfillset(&sact.sa_mask);
    /* Definimos la función callback */
    sact.sa_sigaction = funcion;
    /* Queremos usar el callback sigaction en lugar del dado por signal() */
    sact.sa_flags     = SA_SIGINFO;

    return sigaction(signum, &sact, NULL);
}

En esta función es importante poner bien el prototipo de la función como parámetro, eso lo comenté en un post anterior sobre callbacks

Ahora, vamos a dejar el código más bonito, este programa implementará el control de errores y nos permitirá salir cuando recibamos 5 SIGINT (pulsemos 5 veces control+C):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

void error(char *cadena);
int sigact(int signum, void funcion(int, siginfo_t *, void *));
void llegasigint();
void recibe_signal(int signum, siginfo_t *siginfo, void *context);

int main(int argc, char *argv[])
{
  if (sigact(SIGUSR1, recibe_signal)==-1)
    error("No puedo establecer SIGUSR1");

  if (sigact(SIGINT, recibe_signal)==-1)
    error("No puedo establecer SIGINT");
  if (sigact(SIGTERM, recibe_signal)==-1)
    error("No puedo establecer SIGTERM");
  if (sigact(SIGUSR2, recibe_signal)==-1)
    error("No puedo establecer SIGUSR2");
  if (sigact(SIGQUIT, recibe_signal)==-1)
    error("No puedo establecer SIGQUIT");

    /* Bucle infinito, para cerrar el proceso, utilizar kill -9 PID */
    while (1)
      pause();

    return 0;
}

void error(char *cadena)
{
  fprintf(stderr, "ERROR: %s (errno: %d: %s)\n", cadena, errno, strerror(errno));
  exit(1);
}

void llegasigint()
{
  static int num=0;     /* No he querido usar variables globales */

  num++;
  if (num==5)
    {
      printf ("FIN del programa\n");
      exit(1);
    }
}

void recibe_signal(int signum, siginfo_t *siginfo, void *context)
{
  /* Mostramos toda la información necesaria */
  printf ("[%d] Recibida %d (%s) de %d (UID: %d) ¿Error? %d \n",
      getpid(),     /* PID del proceso actual */
      signum,       /* Número de señal recibida */
      strsignal(signum),    /* Señal escrita en texto (inglés) */
      siginfo->si_pid,  /* PID del proceso que nos manda la señal */
      siginfo->si_uid,  /* Usuario que nos manda la señal */
      siginfo->si_errno);   /* Error producido */

  if (signum==SIGINT)
    llegasigint();
}

int sigact(int signum, void funcion(int, siginfo_t *, void *))
{
    struct sigaction sact;
   
    sigfillset(&sact.sa_mask);
    /* Definimos la función callback */
    sact.sa_sigaction = funcion;
    /* Queremos usar el callback sigaction en lugar del dado por signal() */
    sact.sa_flags     = SA_SIGINFO;

    return sigaction(signum, &sact, NULL);
}

Instalar y configurar un servidor LAMP

Lunes, 17 de Octubre de 2011 Gaspar Fernández Sin comentarios

Lámpara de bajo consumoEn esta ocasión el servidor LAMP a instalar corresponderá con Linux + Apache + MySQL + PHP, intentaré dejarlo todo funcionando.

Éste es el servidor local que suelo utilizar para mis proyectos web, de todas formas no me quiero meter en necesidades muy avanzadas, ni de seguridad (es un servidor local para hacer pruebas, desarrollar webs, y correr servicios locales; no un servidor de producción), si el servidor está cerrado al exterior, mejor. Esta guía dejará el servidor operativo, lo que hagamos con él, es cosa nuestra.

En este tutorial tocaré los siguientes puntos:

Instalación de software

En este punto, supongo que tenéis una máquina GNU/Linux instalada, si necesitas ayuda para instalar una distribución, puedes probar con este enlace.
Esta parte, será especial para los derivados de Debian o Ubuntu, ya que el nombre de los paquetes es especial para dicha distribución. Lo instalamos desde terminal:

$ sudo apt-get install libapache2-mod-php5 php5-gd libgd-tools php5-curl php5-mcrypt php-pear mysql-server php-mysql

Con esto se instalarán más paquetes y dependencias que serán necesarios para nuestro servidor, además, cuando se esté instalando el servidor MySQL nos preguntará qué contraseña queremos para root:
Root MySQL
No tiene que ser la misma que root de nuestro sistema, será una clave para controlar todas las bases de datos del servidor.

apache2: Could not reliably determine the server’s fully qualified domain name, using 127.0.1.1 for ServerName

Si nada más instalar tu servidor web dice esto, pasar no pasa nada, pero molesta un poco que de un fallo, para ello tenemos que editar /etc/apache2/httpd.conf y escribir lo siguiente:

ServerName localhost

Reiniciamos el servidor y ya está todo correcto:

$ sudo service apache2 restart

Creando el directorio de trabajo

Yo utilizo esto para tener las webs alojadas en /home, ya que lo tengo montado en la partición más grande de mi disco duro. Aquí tenemos varias opciones:

  • En el directorio personal de nuestro usuario principal (es rápido y no está mal si sólo nosotros usamos el ordenador). Por ejemplo /home/usuario/proyectos/www/
  • Crear un directorio en /home; por ejemplo /home/servidor/www/
    • Tras ello, debemos darle privilegios de escritura al usuario con el que vamos a trabajar.
    • O por el contrario, si trabajarán varios usuarios, podemos crear un grupo (por ejemplo, desarrolloweb), dar privilegios de escritura al grupo y añadir todos los usuarios a ese grupo.

      $ sudo groupadd desarrolloweb
      $ sudo mkdir -p /home/servidor/www
      $ sudo chgrp -R desarrolloweb /home/servidor/www
      $ sudo gpasswd -M usuario1,usuario2,usuario3,…,usuarioN desarrolloweb

  • Crear un usuario (desarrolloweb) y un grupo (desarrolloweb) y dar privilegios a los usuarios que vayan a acceder como en el apartado anterior.
  • Utilizar el mismo usuario con el que se ejecuta apache (apache, httpd, www-data, depende del sistema, podemos mirar el archivo /etc/passwd para ver todos los usuarios). Para ese usuario crear un directorio en /home, asignar el grupo a desarrolloweb e introducir usuarios en ese grupo:

    $ sudo mkdir -p /home/servidor/www/
    $ sudo usermod -d /home/servidor/
    $ sudo chown -R www-data:desarrolloweb /home/servidor
    $ sudo gpasswd -M usuario1,usuario2,usuario3,…,usuarioN desarrolloweb

    Este último método puede sernos útil cuando nuestros scripts escriban en archivos dentro de nuestro árbol, siempre que esos directorios tengan a www-data como usuario

  • Normalmente, nuestra instalación de apache creará también un grupo llamado www-data (o apache, o httpd, etc), por lo que podemos añadir a todos los usuarios, en lugar de a desarrolloweb a www-data.
  • Se nos pueden ocurrir muchas más formas de organizarnos.

Nota: si optamos por alguna configuración de grupo, tenemos que dar permisos 775 a los directorios del servidor. Y cuando queramos que algún archivo sea modificado/creado por el servidor web debemos darle permiso al este servidor. Si los usuarios que pueden acceder pertenecen al mismo grupo que el servidor web (recordad, www-data, httpd, apache…) con dar permiso al grupo basta, si no, tendremos que modificar los permisos del fichero para permitir a otros la escritura (chmod o+w archivo). Ahora un truco, cuando estemos creando archivos desde terminal, con un usuario perteneciente a desarrolloweb podemos hacer lo siguiente:

$ newgrp desarrolloweb
$ umask 0002

Con esto conseguimos que todos los ficheros que creemos en la sesión pertenezcan al grupo desarrolloweb y tengan los permisos 664 (lectura y escritura para propietario y grupo y lectura para otros) y los directorios, 775 (lectura, escritura y ejecución (o entrada) para propietario y grupo, lectura y entrada para otros).

Una vez tenemos el directorio de trabajo creado, podemos crear enlaces a el dentro de los directorios personales de los usuarios que van a trabajar para que tengan un acceso más rápido, por ejemplo, para mi usuario, crearé un enlace a /home/gaspy/proyectos/www:

$ ln -s /home/servidorweb/www /home/gaspy/proyectos/www

Cambiando el directorio de trabajo en el servidor web

Una vez tenemos nuestro directorio de trabajo, tenemos que decirle a Apache que lo use, por defecto utilizará /var/www ; queremos utilizar nuestro directorio de trabajo. Los valores por defecto no los tocaremos, por si algún día queremos utilizarlos como plantilla ( y por si alguna actualización los re-escribe). El método que muestro es más o menos general, aunque tal vez dependiendo de la distribución varíe algún nombre de archivo:

$ cd /etc/apache2
$ sudo a2dissite 000-default

La utilidad a2dissite elimina el enlace 000-default (dentro de /etc/apache2/sites-enabled/) a sites-available/default, por lo que copiaremos ese último y crearemos otro enlace en sites-enabled para que sea nuestro sitio principal (ayudándonos de la utilidad a2ensite). Tanto a2dissite como a2ensite son dos scripts que nos facilitan un poco la vida a la hora de manejar esos enlaces.

$ sudo cp sites-available/default sites-available/trabajo
$ sudo a2ensite trabajo
$ sudo nano sites-available/trabajo

Ahora debemos modificar algunas líneas de ese documento relativas a la ruta de trabajo, sustituyendo donde ponga /var/www por /home/servidorweb/www dejándolas así:

DocumentRoot /home/servidorweb/www
<Directory /home/servidorweb/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>

Aunque más adelante modificaremos alguna línea más en este archivo (podemos hacerlo todo seguido, pero para este artículo prefiero indicarlo paso a paso), salvamos y salimos (en nano, Control-X).

Probando el servidor

Vamos a introducir un archivo index.php en /home/servidorweb/www/ para que se vea cuando accedemos al raíz de nuestro servidor. Sólo por hacer la prueba, puedes saltar este paso si quieres. El archivo contendrá (debéis sustituir UTF-8 por la codificación con la que creéis el archivo, y la zona horaria Europe/Madrid por la zona horaria donde estéis):

1
2
3
4
5
<?php
   date_default_timezone_set('Europe/Madrid');
   echo "<h1>Funciona!</h1>";
   echo '<p>'.htmlentities('El servidor está funcionando. Son las: '.date('H:i').' del día '.date('d/m/Y'), ENT_COMPAT, 'UTF-8').'</p>';
?>

Para ver el resultado, si estáis en el mismo ordenador donde habéis instalado el servidor, acceded a http://localhost/ , si no, acceded a través de la IP de la máquina servidor, debería dar un mensaje como este:
Server funcionando

Permitir cambiar las opciones de directorios con .htaccess

Es muy útil si vamos a utilizar mod_rewrite, contraseñas en directorios o modificar filtros, documentos de error, etc. aunque por defecto viene deshabilitada cualquier configuración que podamos hacer desde aquí. Para ello, debemos abrir el mismo archivo de antes /etc/apache2/sites-available/trabajo para edición (igual que antes, y cambiar la directiva AllowOverride de None a All (dentro de Directory /home/servidorweb/www), al final se quedaría así:

<Directory /home/servidorweb/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>

Seguramente haya otra etiqueta <Directory>, en este caso ““, con otra directiva AllowOverride que podemos dejar a None, esto es para cuando haya un archivo .htaccess en un nivel superior a nuestro directorio de trabajo, por ejemplo en /home/servidorweb; si ponemos la directiva a “All”, sí que se procesará ese archivo, aunque en este caso, si va a ser una configuración común a todo el servidor (ya que afectará a todos los subdirectorios que tengamos exportados) podemos ponerla en /etc/apache2/sites-available/trabajo.

Configurar mod-rewrite

$ sudo a2enmod rewrite

Ejecuta un script que habilita el módulo especificado. Para ver un listado de los módulos especificados podemos mirar en el directorio /etc/apache2/mods-available ; este script hará un enlace de un módulo en /etc/apache2/mods-enabled. Desde aquí podemos activar el módulo que queramos. Para desactivar ejecutamos como root a2dismod. De forma muy parecida a como hicimos con el sitio web.

Instalamos phpmyadmin

Podemos instalarlo automáticamente con nuestra distribución, por ejemplo en Debian/Ubuntu y derivados:

$ sudo apt-get install phpmyadmin

Nos pedirá la contraseña de la base de datos y listo.

Personalmente, no me gusta que mi distribución haga ciertos cambios y este es uno de ellos, me gusta hacer la instalación de phpMyAdmin de forma manual, así como la de ciertos scripts y utilidades del servidor… en ocasiones edito algún archivo de configuración por nfs y tengo un acceso directo a ellos, además, me gusta complicarme un poco la vida :)

Lo primero es descargarlo, de la web oficial. La última versión hoy es la 3.4.5 (bonito número) por tanto lo instalaremos. Primero creo un directorio para las utilidades de mi servidor local, y tras ello descomprimo ahí phpmyadmin; el archivo comprimido de phpMyAdmin se encuentra en /home/servidorweb/targz:

$ cd /home/servidorweb
$ mkdir util
$ cd util
$ tar xvzf ../targz/phpMyAdmin-3.4.5-all-languages.tar.gz

Se descomprimirá todo en el directorio phpMyAdmin-3.4.5-all-languages/ que renombraremos a pma (PhpMyAdmin), aunque el nombre es lo de menos, sólo queremos un acceso fácil:

$ mv phpMyAdmin-3.4.5-all-languages pma

A continuación vamos a dar acceso al servidor web al directorio /home/servidorweb/util/ ya que está fuera de /home/servidorweb/www/ que es el que tiene acceso desde web. Para ello creamos un nuevo archivo en /etc/apache2/conf.d/ llamado, por ejemplo utilidades.conf, y contendrá lo siguiente:

Alias /util/ “/home/servidorweb/util/”

<Directory “/home/servidorweb/util”>
AllowOverride None
Order allow,deny
Allow from all
</Directory>

Para ver los cambios recargamos la configuración del servidor:

$ sudo service apache2 reload

En este punto, si accedemos desde el navegador a nuestro servidor en la ruta donde hemos vinculado phpmyadmin, por ejemplo http://localhost/util/pma/ veremos:
pma
Ya tenemos phpMyAdmin funcionando, aunque falta un pequeño detalle (no nos impide en cierto modo trabajar, pero ya que lo instalamos lo instalamos bien). phpMyAdmin tiene funciones avanzadas como la creación de PDFs, diseñador de bases de datos, historial y algunas cosas más y para ello necesita que creemos una base de datos e instalemos algunas tablas en ella, para ello, primero crearemos el usuario pma en nuestra base de datos, lo haremos desde el comando mysql:

$ mysql -u root -p

Enter password: [introducimos la contraseña, la primera que introdujimos al principio del tutorial]
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 104

mysql> CREATE USER ‘pma’@'localhost’ IDENTIFIED BY ‘axK12jMf’
Query OK, 0 rows affected (0.03 sec)
mysql> exit

La contraseña podemos generarla con pwgen, cuando terminemos de configurar esto, no la necesitaremos para nada.
Ahora cargamos en la base de datos las tablas de phpMyAdmin (las encontramos dentro del directorio de instalación):

$ mysql -u root -p < /home/servidorweb/util/pma/scripts/create-tables.sql

Ahora damos privilegios al usuario pma para la base de datos phpmyadmin:

$ mysql -u root -p
mysql> GRANT SELECT, INSERT, DELETE, UPDATE ON `phpmyadmin`.* TO ‘pma’@'localhost’;
mysql> exit

Para terminar de configurar debemos crear el fichero config.inc.php, aunque phpMyAdmin trae un fichero llamado config.sample.inc.php que nos puede servir de plantilla. Para ello:

$ cd /home/servidorweb/util/pma/
$ cp config.sample.inc.php config.inc.php

Y editamos el archivo. Podemos ir a http://localhost/util/pma/setup/ y hacer algunos ajustes, luego podemos copiar el archivo generado en config.inc.php o, de manera manual, editando el archivo, para ello, lo primero que debemos rellenar es la línea de blowfish_secret, será una frase secreta para la autentificación de tipo cookie de la aplicación; podemos poner una frase cualquiera, lo primero que se nos ocurra, el caso es que sea secreto, podemos generarlo si queremos con:

$ pwgen -yns 46 1

El tema es que se quede algo como:

1
$cfg['blowfish_secret'] = 'Frase secreta que me acabo de inventar'; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */

Ahora, debemos rellenar las opciones del servidor MySQL, para ello (podemos copiar y pegar, aunque viene comentado en el archivo):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$cfg['Servers'][$i]['controluser'] = 'pma';
$cfg['Servers'][$i]['controlpass'] = 'CLAVE PMA';

/* Storage database and tables */
$cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
$cfg['Servers'][$i]['bookmarktable'] = 'pma_bookmark';
$cfg['Servers'][$i]['relation'] = 'pma_relation';
$cfg['Servers'][$i]['table_info'] = 'pma_table_info';
$cfg['Servers'][$i]['table_coords'] = 'pma_table_coords';
$cfg['Servers'][$i]['pdf_pages'] = 'pma_pdf_pages';
$cfg['Servers'][$i]['column_info'] = 'pma_column_info';
$cfg['Servers'][$i]['history'] = 'pma_history';
$cfg['Servers'][$i]['tracking'] = 'pma_tracking';
$cfg['Servers'][$i]['designer_coords'] = 'pma_designer_coords';
$cfg['Servers'][$i]['userconfig'] = 'pma_userconfig';

Arriba del todo, en la segunda línea, debemos cambiar el valor por la clave que le asignamos al usuario pma.
Por otra parte, si utilizamos la base de datos adicional (phpmyadmin), necesitamos utilizar la extensión mysqli de php en lugar de mysql (puede haber problemas si no lo hacemos):

1
2
/* Select mysqli if your server has it */
$cfg['Servers'][$i]['extension'] = 'mysqli';

Si queremos, podemos incluir servidores a la lista de autentificación inicial de phpMyAdmin, con lo que podremos acceder sin contraseña a nuestro servidor (como root o como el usuario que elijamos), puede ser un método un tanto inseguro, pero si sólo estamos nosotros y tenemos el servidor cerrado al exterior no hay problema. Para ello, debajo de la identificación del primer servidor (las líneas que hemos añadido), añadimos lo siguiente en config.inc.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$i++;
$cfg['Servers'][$i]['verbose'] = 'Servidor Local';
$cfg['Servers'][$i]['host'] = 'localhost';
$cfg['Servers'][$i]['port'] = '';
$cfg['Servers'][$i]['socket'] = '';
$cfg['Servers'][$i]['connect_type'] = 'tcp';
$cfg['Servers'][$i]['extension'] = 'mysqli';
$cfg['Servers'][$i]['auth_type'] = 'config';
$cfg['Servers'][$i]['user'] = 'root';
$cfg['Servers'][$i]['password'] = 'NUESTRA CLAVE DE ROOT DE MYSQL';
$cfg['Servers'][$i]['controluser'] = 'pma';
$cfg['Servers'][$i]['controlpass'] = 'CLAVE PMA';
/* Ponemos esto de nuevo */
/* Storage database and tables */
$cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
$cfg['Servers'][$i]['bookmarktable'] = 'pma_bookmark';
$cfg['Servers'][$i]['relation'] = 'pma_relation';
$cfg['Servers'][$i]['table_info'] = 'pma_table_info';
$cfg['Servers'][$i]['table_coords'] = 'pma_table_coords';
$cfg['Servers'][$i]['pdf_pages'] = 'pma_pdf_pages';
$cfg['Servers'][$i]['column_info'] = 'pma_column_info';
$cfg['Servers'][$i]['history'] = 'pma_history';
$cfg['Servers'][$i]['tracking'] = 'pma_tracking';
$cfg['Servers'][$i]['designer_coords'] = 'pma_designer_coords';
$cfg['Servers'][$i]['userconfig'] = 'pma_userconfig';

Ocultar los parámetros de nuestro proceso a ps

Martes, 11 de Octubre de 2011 Gaspar Fernández Sin comentarios

password_flickrEn ocasiones, estamos desarrollando una aplicación, y ésta necesita que le pasemos como parámetro, por ejemplo, una contraseña. El gran peligro que esto tiene es que cualquier usuario, pidiendo un listado de procesos con ps podrá ver la contraseña.

Imaginemos que tenemos una aplicación (que hemos hecho nosotros) que conecta con un servidor, y dado que hemos hecho un script para automatizar el proceso, el nombre de usuario y contraseña los pasaremos como parámetro al ejecutable. Dicho proceso se llama “aplicacionsegura”, dicho programa lo hemos lanzado en un servidor y otros usuarios tendrán acceso a dicha máquina. Ahora uno de ellos ejecuta lo siguiente:

$ ps ax
….
10560 pts/3    S+     0:00 ./aplicacionsegura –user=usuario –passwd=micontraseña

¡Nuestro gozo en un pozo! Otro usuario ya tiene nuestra contraseña, y ha sido muy sencillo. Y además lo podremos ver (de hecho ps es lo que hace) viendo el archivo /proc/PID/cmdline. Aunque debemos ver qué opciones tenemos:

Modificar el parámetro passwd

Para ilustrar el ejemplo, vamos a fijar la posición de –passwd=xxxxx al segundo parámetro (la captura de parámetros no es objeto de este post). Lo primero que vamos a probar es introducir un terminador en las cadenas de los parámetros, Si la cadena empieza por ‘\0′ estará vacía:

1
2
3
4
...
argv[1][0]='\0';
argv[2][0]='\0';
....

Aunque tardaremos poco en darnos cuenta que da igual, por una parte, no podríamos borrar todos los rastros de información, ya que si escribimos en la posición 0 de la cadena “–passwd=micontraseña” un terminador, el resto de la cadena se quedará en memoria y podrá ser leída. De todas formas, la cadena parece que sólo tiene un espacio para ps, es más, el \0 le ha dado igual.

Probemos ahora escribiendo “HOLA” en la cadena de la contraseña (ahora sí pongo el código completo, test.c):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
  /* Reservamos memoria de sobra */
  char password[40];
  char usuario[40];

  if (argc<3)
    {
      fprintf(stderr,"Debe tener dos parámetros:\n%s --user=USUARIO --passwd=CLAVE\n", argv[0]);
      return EXIT_FAILURE;
    }

  /* Esto es un simple ejemplo, vamos a quitar --user y --passwd de una forma fea */

  /* Copiamos el usuario en otra variable */
  strcpy(usuario, argv[1]+7);   /* +7, para eliminar --user= (7 caracteres) */

  /* Copiamos la contraseña en otra variable */
  strcpy(password, argv[2]+9);  /* +9, para eliminar --passwd= (9 caracteres) */

  strcpy(argv[2]+9, "HOLA"); // EN LA CLAVE PONEMOS "HOLA"

  printf("user: %s ; pass: %s", usuario, password);
  /* Esperamos una tecla para terminar (sólo para que no se cierre demasiado rápido */
  /* y nos dé tiempo a mirar la información de ps */
  getchar();
  return EXIT_SUCCESS;
}

Ahora ejecutaremos lo siguiente:

$ gcc -o test test.c
$ ./test & # El programa test esperará una tecla para terminar, pero lo ejecutaremos de fondo, para que no se cierre.
$ ps ax
….
10809 pts/3 S+ 0:00 ./test –user=usuario –passwd=HOLA traseña
….
$ killall test # Para matar el proceso

Un efecto curioso, las cadenas de los parámetros no son NULL-terminated, de cara a /proc/PID/cmdline por lo que cuando ponemos un terminador, lo toma como un espacio. Y se ve parte del parámetro. Lo que sí podemos hacer es sustituir por asteriscos, tantos asteriscos como letras tenga el parámetro.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void rellenaConAsteriscos(char *cadena);

int main(int argc, char *argv[])
{
  /* Reservamos memoria de sobra */
  char password[40];
  char usuario[40];

  if (argc<3)
    {
      fprintf(stderr,"Debe tener dos parámetros:\n%s --user=USUARIO --passwd=CLAVE\n", argv[0]);
      return EXIT_FAILURE;
    }

  /* Esto es un simple ejemplo, vamos a quitar --user y --passwd de una forma fea */

  /* Copiamos el usuario en otra variable */
  strcpy(usuario, argv[1]+7);   /* +7, para eliminar --user= (7 caracteres) */

  /* Copiamos la contraseña en otra variable */
  strcpy(password, argv[2]+9);  /* +9, para eliminar --passwd= (9 caracteres) */

  rellenaConAsteriscos(argv[1]+7);
  rellenaConAsteriscos(argv[2]+9);

  printf("user: %s ; pass: %s", usuario, password);
  /* Esperamos una tecla para terminar (sólo para que no se cierre demasiado rápido */
  /* y nos dé tiempo a mirar la información de ps */
  getchar();
  return EXIT_SUCCESS;
}

void rellenaConAsteriscos(char *cadena)
{
  while (*cadena!='\0')
    *(cadena++)='*';
}

Ahora al ejecutar ps:

$ ps ax

10906 pts/3 S+ 0:00 ./test –user=******* –passwd=*************

Aunque esta forma revela en cierto modo la longitud de las contraseñas y el nombre de usuario, puede que no nos interese, que queramos eliminar el parámetro entero y que no se vea:

La manera rápida pero peligrosa y de andar por casa

El objetivo es escribir algo más largo que el último parámetro. Es decir, si el parámetro de contraseña tiene 22 letras, tenemos que escribir algo más grande, unas 23 letras, o cargarnos el terminador del segundo argumento (no son NULL-terminated, el kernel sabe dónde empieza y dónde termina la cadena, pero hay un NULL para comprobar que todo va bien).

En este caso podemos hacer:

1
strcpy(argv[2]+9, "Esto es una cadena muy grande para ver si cargándome el segundo parámetro desaparece toda la información");

y veremos cómo ps no nos dice nada, en principio no es demasiado elegante, nos estamos cargando un terminador, que nosotros podríamos necesitar, para lectura, y estamos escribiendo en una zona de memoria que no es nuestra, ya que el argumento tiene reservado un tamaño de memoria, y estamos escribiendo más de lo que debemos (podemos producir una violación de segmento).

Decirle al kernel que no queremos los parámetros

Parecida a la forma anterior, podemos hacer:

1
argv[2][strlen(argv[2])]=' ';

lo que no es intuitivo para nada, porque nos estamos cargando el terminador, que controla el paso de parámetros. De esta forma, no tendremos ningún parámetro. Tambén podemos hacer una forma general

1
argv[argc-1][strlen(argv[argc-1])]=' ';

De esta forma desaparecerán todos los argumentos de /proc/PID/cmdline ; sólo tendremos la ruta y el nombre del ejecutable, y nuestros datos, al menos por esta parte, estarán seguros.
Foto: marc falardeau (Flickr)

Buscando un proceso en C

Martes, 4 de Octubre de 2011 Gaspar Fernández 2 comentarios

Comando topEn ocasiones, nuestros programas requieren que un servicio o un programa esté en ejecución. Algunos servicios los podemos ubicar fácilmente, ya que /var/run ,  /dev/shm u otra ruta contienen un archivo con su PID (Identificador de proceso), otras servicios no figuran en ningún lado. También puede ser que estemos esperando que otro proceso termine y necesitamos averiguar su PID.

Para todo ello, debemos echar un ojo al contenido de /proc/, ahí encontramos, entre otras cosas, información sobre los procesos en ejecución del sistema. En principio lo que nos interesa es el nombre del ejecutable (es lo que estamos buscando), para ello, vamos a buscar dentro del directorio /proc/ todos los directorios que sean numéricos (esos que contendrán un PID), y leeremos el fichero /proc/[pid]/comm, que contiene la información del nombre del ejecutable. Eso sí, ese nombre tendrá una longitud limitada (marcada por la constante del kernel TASK_COMM_LEN, que suele valer 16 (con lo que los nombres tendrán como máximo 15 caracteres):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>

void tareaProceso(char *nombre, int pid);

void buscaProceso(char *nombre);

void error(char *error);

/* Una función lo más general posible */
void getProcName(char *dest, int pid);

int main(int argc, char *argv[])
{
  buscaProceso("firefox-bin");
  return EXIT_SUCCESS;
}

void tareaProceso(char *nombre, int pid)
{
  printf("Encontrado proceso %-16s con PID: %d\n", nombre, pid);
  /* Aquí ya puedo matarlo, mandarle señales o hacer lo que quiera */
  /* con el proceso en cuestión */
}

void buscaProceso(char *nombre)
{
  DIR *proc;
  struct dirent *proceso;
  char procname[16];        /* Nombre del proceso */
  int pid;

  proc=opendir("/proc");

  if (proc==NULL)
    error("No puedo acceder al directorio /proc");

  while ((proceso=readdir(proc)) != NULL)
    {
      /* En /proc hay más cosas además de los PIDs, debemos asegurarnos de que */
      /* lo que hemos visto es un ID de proceso, lo demás no nos interesa */
      if ( (*proceso->d_name>'0') && (*proceso->d_name<='9') )
    {
      /* Convertimos el PID a número para la llamada a la función */
      pid=strtol(proceso->d_name, NULL, 10);
      getProcName(procname, pid);
      if (strncmp(nombre,procname,15)==0)
        tareaProceso(procname, pid);
    }
    }
}

void error(char *error)
{
  fprintf(stderr, "Error: %s (%d, %s)\n", error, errno, strerror(errno));
  exit(EXIT_FAILURE);
}

void getProcName(char *dest, int pid)
{
  FILE *fcomm;
  char filename[20];        /* /proc/[PID] (5 chars)/comm : total 16 chars */

  sprintf(filename,"/proc/%d/comm", pid);

  fcomm=fopen(filename, "r");
  if (!fcomm)
    error("No existe el fichero");
  /* A veces fgets() coge un salto de línea extra */
  /* fgets(dest, 16, fcomm); */
  fscanf(fcomm, "%s", dest);
  close(fcomm);
}

En este ejemplo si comentamos la línea 52 y en la línea 53 ponemos:

1
tareaProceso(nombre, pid);

podemos obtener un listado completo de las tareas en ejecución (sólo por curiosear un poco). Aunque en este ejemplo tenemos un problema, mencionado antes, el número de caracteres del ejecutable, es decir, hay ejecutables con más de 15 caracteres, y tal vez queramos buscarlos. Con la solución actual estamos cortando los nombres (con strncmp()), es decir, sería lo mismo aplicacionconnombrelargo que aplicacionconnombrecorto, ya que sólo tenemos en cuenta los primeros 15 caracteres. Tiene que haber otra forma de sacar el nombre completo, y es a través del archivo /proc/[PID]/cmdline, es más, desde aquí podemos sacar la ruta del ejecutable y los parámetros con los que se llamó, tal y como vemos cuando ejecutamos:

$ ps x

Vamos a ver cómo podemos adaptar el código de antes para poder sacar toda esa información.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <libgen.h>     /* basename */

enum pnametype
  {
    SHORT,
    LONG,
    WPATH
  };

/* Realiza una tarea con el proceso encontrado */
void tareaProceso(char *nombre, int pid);

/* Busca un proceso determinado */
void buscaProceso(char *nombre);

/* Sale del programa emitiendo un error */
void error(char *error);

/* Obtiene el nombre de un proceso */
void getProcName(char *dest, int pid, enum pnametype outtype);

/* Lee los contenidos de un archivo */
int getFileContents(char *dest, char *fname);

int main(int argc, char *argv[])
{
  buscaProceso("plugin-container");
  return EXIT_SUCCESS;
}

void tareaProceso(char *nombre, int pid)
{
  printf("Encontrado proceso %-16s con PID: %d\n", nombre, pid);
  /* Aquí ya puedo matarlo, mandarle señales o hacer lo que quiera */
  /* con el proceso en cuestión */
}

void buscaProceso(char *nombre)
{
  DIR *proc;
  struct dirent *proceso;
  char procname[255];       /* Como getProcName necesita una variable grande, se la damos */
  int pid;

  proc=opendir("/proc");

  if (proc==NULL)
    error("No puedo acceder al directorio /proc");

  while ((proceso=readdir(proc)) != NULL)
    {
      /* En /proc hay más cosas además de los PIDs, debemos asegurarnos de que */
      /* lo que hemos visto es un ID de proceso, lo demás no nos interesa */
      if ( (*proceso->d_name>'0') && (*proceso->d_name<='9') )
    {
      /* Convertimos el PID a número para la llamada a la función */
      pid=strtol(proceso->d_name, NULL, 10);
      getProcName(procname, pid, LONG);
      if (strcmp(nombre,procname)==0)
        tareaProceso(procname, pid);
    }
    }
}

void error(char *error)
{
  fprintf(stderr, "Error: %s (%d, %s)\n", error, errno, strerror(errno));
  exit(EXIT_FAILURE);
}

int getFileContents(char *dest, char *fname)
{
  FILE *fdata;
  int res;

  fdata=fopen(fname, "r");
  if (!fdata)
    error("No existe el fichero");
 
  res=fscanf(fdata, "%s", dest);
  if (res==EOF)         /* Si no hemos leído nada,  */
    *dest='\0';         /* nos aseguramos de vaciar la cadena */

  fclose(fdata);

  /* Devolvemos un error si, por ejemplo el fichero está vacío */
  return (res!=EOF);
}
/* dest tiene que ser grande, en un proyecto real, reservamos memoria desde */
/* getProcName, no nos arriesgamos a que falle algo de fuera */
void getProcName(char *dest, int pid, enum pnametype outtype)
{
  char filename[20];        /* /proc/[PID] (5 chars)/cmdline : total 19 chars */
  char *tmp;
  char *newfile;
  if (outtype==SHORT)
    sprintf(filename,"/proc/%d/comm", pid);
  else
    sprintf(filename,"/proc/%d/cmdline", pid);

  if (!getFileContents(dest, filename))
    {
      /* Si no hemos podido obtener nada */
      if (outtype!=SHORT)
    {
      /* probamos desde comm */
      sprintf(filename,"/proc/%d/comm", pid);
      getFileContents(dest, filename);
    }
    }

  if (outtype!=SHORT)
    {
      /* Si queremos un tipo de salida que no sea el corto, tendremos que transformar esta cadena */
      tmp=dest;
      while ((*tmp!=' ') && (*tmp!='\0')) ++tmp;
      tmp='\0';
      if (outtype==LONG)
    {
      tmp=basename(dest);
      strcpy(dest, tmp);
    }
    }
}

Ahora, obtenemos la información dependiendo del tipo especificado por outtype de tipo enum pnametype, e incluso si especificamos LONG, este tipo extraerá el nombre del archivo ejecutable despreciando la ruta del mismo. Para descargar los archivos de código, click aquí (2.3Kb)

Guía de supervivencia para disfrutar de Linux Mint Debian Edition XFCE (IV : Soporte de idioma, tipografías y plugins de XFCE, complementos, efectos de escritorio con Compiz Fusion)

Jueves, 29 de Septiembre de 2011 Gaspar Fernández Sin comentarios

Hasta ahora, aunque en la instalación especificamos que queríamos el sistema en idioma español, era sólo para la instalación y algunas aplicaciones que ya instalaban el soporte para varios idiomas. Esto es así porque la instalación no requería de conexión a Internet, y en el DVD de instalación no se incluían los idiomas adicionales. Por ello tendremos que instalarlos.

Este post será largo, quiero incluir detalles de muchas cosas, y pongo aquí el índice:

Instalar paquetes de idioma para varias aplicaciones

A partir de ahora, instalaremos los paquetes de idioma español para Firefox, Thunderbird, LibreOffice y páginas del manual (man):

$ sudo apt-get install firefox-l10n-es thunderbird-l10n-es libreoffice-l10n-es manpages-es

También encontramos paquetes para Chromium o para la plataforma KDESC y muchos más.

Si queremos instalar los soportes de ortografía de Firefox debemos dirigirnos a esta dirección y podremos descargarlo e instalarlo directamente en el navegador.

Configuración de Linux Mint XFCE

Este paquete no viene instalado de serie, aunque estaría bien; a primera vista puede ser parecido a el administrador de configuración de XFCE (menu/Settings o Configuración/Administrador de Configuración; ejecutable: xfce4-settings-manager), aunque tiene algunas cosas más. Nos permite configurar la impresora, dispositivos bluetooth, usuarios, gestor de paquetes, monitor de sistema, diagnóstico de disco, copias de seguridad, etc. En definitiva es un centro de control donde podemos acceder a todo lo que tenemos en menu/Settings o Configuración , pero de una forma organizada.

Cuenta además con un grupo llamado Personalizar que nos permite elegir qué aplicaciones introducimos ahí de forma que tengamos un acceso directo.

xfce4-mintConfig

Para instalarlo,

$ sudo apt-get install mintconfig-xfce

Algo destacable acerca de esta aplicación y de los componentes que instala es que:

  • Permite configurar el sistema para iniciar los efectos de escritorio
  • Tiene herramientas para configurar recursos de red compartidos
  • Editor de configuración avanzada de XFCE.
  • Configuración de NDISwrapper (Controladores inalámbricos de Windows)
  • Copia de seguridad de tu home (mintBackup)
  • Bloqueador de dominios (mintnanny)
  • Soluciones a problemas con tipografías en algunos monitores a un click.

Si te gusta personalizar tu ordenador, es conveniente tenerlo a mano.

Instalar tipografías adicionales

Las tipografías suelen ser una parte importante para todos aquellos que diseñamos, escribimos, compartimos… el tipo de letra en sí expresa ya parte del mensaje. He oído varias veces decir que “instalar tipos de letra en Linux es complicado y que no hay tantas opciones como en otros sistemas”, cuando los formatos para los tipos de letra son los mismos para todos. Aquí instalaremos casi 600 tipografías ttf (TrueType)  sin complicarnos mucho la vida. Y ahora deberemos pasar al menos un par de días mirando todas las fuentes instaladas (muchas de ellas son libres).Tipografías

$ sudo apt-get install ttf-summersby ttf-ubuntu-title ttf-prociono ttf-liberation ttf-linux-libertine ttf-sjfonts ttf-okolaks ttf-opendin ttf-mplus ttf-georgewilliams ttf-femkeklaver ttf-engadget  ttf-dustin ttf-droid  ttf-breip  ttf-century-catalogue  ttf-comfortaa  ttf-baekmuk  ttf-ancient-fonts  ttf-adf-verana  ttf-aenigma  ttf-alee ttf-adf-baskervald  ttf-adf-libris ttf-bitstream-vera ttf-isabella ttf-xfree86-nonfree

Además, si alguien echa de menos los tipos de letra “de toda la vida” de Windows (Arial, Times New Roman, Verdana, Trebuchet), podemos instalarlos de la siguiente manera:

$ sudo apt-get install ttf-mscorefonts-installer

Complementos para el panel de XFCE

Los paneles pueden hacer mucho más de lo que hacen por defecto, por eso vamos a personalizarlos y a añadir complementos nuevos. Empecemos por el panel de arriba donde están los programas, la lista de escritorios y el botón de menú (arriba a la izquierda).

El primer paso, para añadir elementos es hacer click con el botón derecho en el panel > Panel > Añadir nuevos elementos:

PanelTras ello aparecerá una nueva ventana donde seleccionaremos el complemento a instalar, como primer complemento adicional escogeremos un control de volumen.

screenshot-17-09-2011-010935

Vamos a instalar unos cuantos plugins, aunque muchos de ellos los dejo para que cada uno descubra los que más le gustan o le resulten más útiles:

$ sudo apt-get install xfce4-clipman xfce4-clipman-plugin xfce4-cpugraph-plugin xfce4-genmon-plugin xfce4-mount-plugin xfce4-notes-plugin xfce4-notes xfce4-systemload-plugin xfce4-smartbookmark-plugin xfce4-timer-plugin  xfce4-verve-plugin xfce4-wmdock-plugin xfce4-datetime-plugin

Los cambios que diré ahora son algunas propuestas que a mí me resultan útiles:

  • Cambio del plugin por defecto de hora por el plugin “Fecha y hora”. Para ello debemos pulsar sobre el elemento actual de hora y elegimos “Quitar” del menú contextual. Luego añadir el elemento “Fecha y hora”, este elemento nos dará más información y, además, al hacer click con el botón izquierdo, nos mostrará un calendario.
    Calendario XFCE
  • Notas de escritorio. Para ello añadimos el plugin “Notas”
    Notas
  • . Nos permite ver los dispositivos que tenemos montados y desmontarlos a un clic.
    screenshot-17-09-2011-030935_2Aunque la configuración que viene por defecto no me gusta demasiado, propongo una configuración más práctica.
  • Clipman. Este complemento nos permite almacenar lo que vamos copiando en el portapapeles. Imagina que copiamos un texto, lo pegamos y seguimos trabajando, copiamos otro texto, y ahora necesitamos pegar el primer texto que copiamos. Este complemento nos ayuda a llevar a cabo esas acciones.
    Lo podemos ejecutar también como parte de la bandeja de sistema accediendo a menu/Settings o Configuración/Sesión e Inicio/Autoarranque de Aplicaciones y marcando Clipman.

Otras utilidades de XFCE

Ahora instalaremos algunas aplicaciones para XFCE que nos serán útiles en algunas circunstancias

  • xfce4-volumed . Esta aplicación es muy útil para usuarios de portátiles o teclados multimedia, ya que nos permitirá modificar el volumen del sistema con las teclas específicas para ello.
    Lo instalamos de la siguiente manera:
    $ sudo apt-get install xfce4-volumed

    Linux Mint configura este paquete para que se arranque automáticamente al iniciar XFCE.

  • orage . Es un gestor de calendario donde podemos anotar citas, eventos y tareas. Es muy sencillo, rápido y se integra bien con XFCE.
    Lo instalamos de la siguiente manera:
    $ sudo apt-get install orage
  • xfswitch-plugin . Es otro plugin para el panel que nos permite cambiar la sesión gráfica de usuario o identificarnos como un usuario nuevo sin cerrar la sesión actual. Podremos cambiar entre sesiones con Control+Alt+F7/F8/F9…
    Lo instalamos de la siguiente manera:
    $ sudo apt-get install xfswitch-plugin
  • epdfview . Es un visor de pdfs muy ligero. Tiene menos opciones que evince, pero para un visionado rápido nos vale.
  • pcmanfm . Es un administrador de archivos muy ligero, con soporte para pestañas y acceso a sistemas de archivos remotos (ssh/ftp/…)
    pcmanfmPodemos utilizarlo en lugar de Thunar para abrir directorios si nos vamos a Menu/Settings o Configuración/Aplicaciones preferidas > Utilidades y ahí seleccionamos PCMan File Manager:
    Por defecto PCManFM

Efectos de escritorio con Compiz

Seguro que es una de las partes que más llama la atención, y una de esas cosas que utilizaremos para sorprender a las visitas enseñándoles un entorno gráfico del futuro. Aunque, además de tener un entorno gráfico más amigable, si tenemos una tarjeta gráfica decente (Ati/NVIDIA o incluso Intel), y más o menos nueva (por lo menos de 2004/2005), podemos disfrutar de un mejor rendimiento general del sistema y de un entorno mucho más fluído (ya que dejamos algunas tareas más para la GPU) y tendremos un valor añadido en algunos efectos.

Cubo CompizYo no soy muy partidario de efectos elaborados y lentos, aunque el cubo de escritorios es una excepción, aunque lo tengo configurado para que tarde menos de medio segundo. Aunque tenemos otros efectos útiles como por ejemplo hacer Zoom a la pantalla (muy útil para diseñadores en algunas ocaiones, ¿quién no se ha acercado tanto al monitor para saber si dos líneas son paralelas?), o por ejemplo una previsualización de la ventana al pasar el ratón por encima de la barra de tareas.

Para instalar:

$ sudo apt-get install compiz-gtk compiz compizconfig-settings-manager  compiz-fusion-plugins-extra compiz-fusion-plugins-main

Ahora podremos habilitar Compiz desde la configuración de Linux Mint (xfce4-mintconfig), seleccionando Xfce mintDesktop y dentro del nuevo diálogo, en la pestaña Compiz Fusion, habilitando Compiz Fusion.

Configurar CompizAhora, debemos ir a la configuración de Compiz, Compiz Config Settings Manager, o CCSM; podemos hacerlo ejecutando el comando ccsm, o desde la misma pantalla de configuración de Linux Mint pulsando en Configurar Compiz con CompizConfig.

ccsmA partir de aquí os dejo para configurar vuestros efectos de escritorio a medida.

Apéndice I: Configurando el plugin Mount Devices

Configurar esta extensión no es complicado. Si vimos la captura anterior, se mostraban todos los puntos de montaje (yo tengo muchas particiones, para /usr, /var, /boot, etc y no me interesa desmontarlas, es más, no quiero verlas ahí; por otra parte, puede que utilice recursos de red y sí que quiera desmontar estos. Empecemos haciendo clic con el botón derecho del ratón y seleccionando Propiedades:

Mount props1Podemos mostrar un mensaje nada más desmontar el dispositivo, pero mejor desmarcamos esa casilla, ya que vamos a hacer algo más completo en la siguiente pestaña. También podemos seleccionar un icono, eso es a gusto del usuario.

En la siguiente pestaña, Commands, encontramos comandos personalizados a la hora de montar y desmontar volúmenes.

Command Mount pluginEn este punto, marcaremos la casilla “Custom commands“, para indicar comandos para montar/desmontar unidades manualmente. Los parámetros de montaje (Execute after mounting y Mount command) los podemos dejar en blanco, ya que el montaje se reserva para todos los puntos especificados en /etc/fstab y no para los dispositivos que el sistema vaya descubriendo.

Aunque para Unmount command, yo tengo puesto lo siguiente:

pumount %d && notify-send “%d desmontado correctamente” || notify-send “Hubo un problema al desmontar %d”

Es necesario tener pmount instalado. Con esta línea podremos ver un mensaje como notificación ya haya tenido éxito o no la operación. Esta línea no valdrá para ubicaciones de red aunque podemos vincular la operación a un script más complejo.

Por último, en la pestaña File Systems, podemos indicar qué queremos mostrar y qué no queremos mostrar

File Systems Mount devicesEn esta pestaña, podemos seleccionar la opción de  representar sistemas de archivos de red (Display network file systems), expulsar unidades de CD (Eject CD-drives) mostar sólo los puntos de montaje y no el dispositivo o el UUID, lo cual puede llegar a ser incómodo (Display mount points only), y por último excluir algunos sistemas de archivo (Exclude specified file systems), y es en esa opción donde escribimos separados por espacios los puntos de montaje separados por espacios que no queremos mostrar; en mi caso, son los puntos de montaje del sistema.

Listar archivos dentro de un directorio o carpeta en C

Martes, 27 de Septiembre de 2011 Gaspar Fernández 4 comentarios

Árbol de directoriosNuestro software debe buscar archivos dentro de un directorio determinado, ya sea un archivo especial, una recopilación de datos del disco duro, una búsqueda de plugins, etc…

Hemos elegido lenguaje C para hacer esto, porque en bash podemos llamar a ls o find y apaga y vámonos.

Estas funciones, nos recordarán al uso de archivos con fopen() y fclose().

Para usar esta implementación, como veremos en el ejemplo, debemos incluir <sys/types.c> y <dirent.h> y, en principio, empezaremos con un ejemplo sencillo que lista los archivos del directorio actual (una vez tenemos el nombre y la ruta del archivo podemos hacer con él lo que queramos):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

/* Función para devolver un error en caso de que ocurra */
void error(const char *s);
/* Función que hace algo con un archivo */
void procesoArchivo(char *archivo);

int main()
{
  /* Con un puntero a DIR abriremos el directorio */
  DIR *dir;
  /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */
  struct dirent *ent;

  /* Empezaremos a leer en el directorio actual */
  dir = opendir (".");

  /* Miramos que no haya error */
  if (dir == NULL)
    error("No puedo abrir el directorio");
 
  /* Una vez nos aseguramos de que no hay error, ¡vamos a jugar! */
  /* Leyendo uno a uno todos los archivos que hay */
  while ((ent = readdir (dir)) != NULL)
    {
      /* Nos devolverá el directorio actual (.) y el anterior (..), como hace ls */
      if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) )
    {
      /* Una vez tenemos el archivo, lo pasamos a una función para procesarlo. */
      procesoArchivo(ent->d_name);
    }
    }
  closedir (dir);

  return EXIT_SUCCESS;
}

void error(const char *s)
{
  /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */
  perror (s);
  exit(EXIT_FAILURE);
}

void procesoArchivo(char *archivo)
{
  /* Para "procesar", o al menos, hacer algo con el archivo, vamos a decir su tamaño en bytes */
  /* para ello haremos lo que vemos aquí: http://totaki.com/poesiabinaria/2010/04/tamano-de-un-fichero-en-c/ */
  FILE *fich;
  long ftam;

  fich=fopen(archivo, "r");
  if (fich)
    {
      fseek(fich, 0L, SEEK_END);
      ftam=ftell(fich);
      fclose(fich);
      /* Si todo va bien, decimos el tamaño */
      printf ("%30s (%ld bytes)\n", archivo, ftam);
    }
  else
    /* Si ha pasado algo, sólo decimos el nombre */
    printf ("%30s (No info.)\n", archivo);
}

Este es un ejemplo sencillo, podemos profundizar un poco más. Vemos que readdir() nos devuelve todo lo que hay, y a veces, es muy útil diferenciar entre un fichero, un directorio, un enlace, un socket, un fifo, etc… para eso, en un principio utilizaremos la información del mismo dirent, que a veces la encontramos de forma rápida, aunque existen sistemas de archivos que no nos facilitan esa información de forma tan inmediata y tenemos que hacer stat(), un ejemplo de ello es nfs, ya que es significativamente más lento obtener esa información, si no la necesitamos, no intenta obtenerla, es para esos casos cuando obligamos a su obtención con stat().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>

/* Función para devolver un error en caso de que ocurra */
void error(const char *s);

/* Calculamos el tamaño del archivo */
long fileSize(char *fname);

/* Sacamos el tipo de archivo haciendo un stat(), es como el stat de la línea de comandos */
unsigned char statFileType(char *fname);

/* Función que hace algo con un archivo, pero le pasamos el dirent completo, usaremos más datos */
void procesoArchivo(char *ruta, struct dirent *ent);

int main(int argc, char *argv[])
{
  /* Con un puntero a DIR abriremos el directorio */
  DIR *dir;
  /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */
  struct dirent *ent;

  if (argc != 2)
    {
      error("Uso: ./directorio_2 <ruta>\n");
    }
  /* Empezaremos a leer en el directorio actual */
  dir = opendir (argv[1]);

  /* Miramos que no haya error */
  if (dir == NULL)
    error("No puedo abrir el directorio");
 
  /* Una vez nos aseguramos de que no hay error, ¡vamos a jugar! */
  /* Leyendo uno a uno todos los archivos que hay */
  while ((ent = readdir (dir)) != NULL)
    {
      /* Nos devolverá el directorio actual (.) y el anterior (..), como hace ls */
      if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) )
    {
      /* Una vez tenemos el archivo, lo pasamos a una función para procesarlo. */
      procesoArchivo(argv[1], ent);
    }
    }
  closedir (dir);

  return EXIT_SUCCESS;
}

void error(const char *s)
{
  /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */
  perror (s);
  exit(EXIT_FAILURE);
}

long fileSize(char *fname)
{
  FILE *fich;
  long ftam=-1;

  fich=fopen(fname, "r");
  if (fich)
    {
      fseek(fich, 0L, SEEK_END);
      ftam=ftell(fich);
      fclose(fich);
    }
  else
    printf("ERRNO: %d - %s\n", errno, strerror(errno));
  return ftam;
}

void procesoArchivo(char *ruta, struct dirent *ent)
{
  long ftam;
  char *nombrecompleto;
  char strtam[20];
  char strtipo[30]="";
  /* Tiene que ser del mismo tipo de dirent.d_type en nuestro sistema */
  static unsigned char tipoID[7]={DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK};
  static char* tipoSTRs[7]={"Dispositivo de bloques", "Dispositivo de caracteres", "Directorio", "FIFO", "Enlace", "Archivo regular", "Socket Unix"};

  int i;
  int tmp;
  unsigned char tipo;

  /* Sacamos el nombre completo con la ruta del archivo */
  tmp=strlen(ruta);
  nombrecompleto=malloc(tmp+strlen(ent->d_name)+2); /* Sumamos 2, por el \0 y la barra de directorios (/) no sabemos si falta */
  if (ruta[tmp-1]=='/')
    sprintf(nombrecompleto,"%s%s", ruta, ent->d_name);
  else
    sprintf(nombrecompleto,"%s/%s", ruta, ent->d_name);

  /* Calcula el tamaño */
  ftam=fileSize(nombrecompleto);
  if (ftam>=0)
    sprintf(strtam, "%ld bytes", ftam);
  else
    strcpy(strtam, "No info");

  /* A veces ent->d_type no nos dice nada, eso depende del sistema de archivos que estemos */
  /* mirando, por ejemplo ext*, brtfs, sí nos dan esta información. Por el contrario, nfs */
  /* no nos la da (directamente, una vez que hacemos stat sí lo hace), y es en estos casos donde probamos con stat() */
  tipo=ent->d_type;
  if (tipo==DT_UNKNOWN)
    tipo=statFileType(nombrecompleto);

  if (tipo!=DT_UNKNOWN)
    {
      /* Podíamos haber hecho un switch con los tipos y devolver la cadena,
         pero me da la impresión de que así es menos costoso de escribir. */

      i=0;
      while ( (i<7) && (tipo!=tipoID[i]) )
    ++i;

      if (i<7)
    strcpy(strtipo, tipoSTRs[i]);
    }

  /* Si no hemos encontrado el tipo, éste será desconocido */
  if (strtipo[0]=='\0')
    strcpy(strtipo, "Tipo desconocido");

  printf ("%30s (%s)\t%s \n", ent->d_name, strtam, strtipo);

  free(nombrecompleto);
}

/* stat() vale para mucho más, pero sólo queremos el tipo ahora */
unsigned char statFileType(char *fname)
{
  struct stat sdata;

  /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */
  if (stat(fname, &sdata)==-1)
    {
      return DT_UNKNOWN;
    }


  switch (sdata.st_mode & S_IFMT)
    {
    case S_IFBLK:  return DT_BLK;
    case S_IFCHR:  return DT_CHR;
    case S_IFDIR:  return DT_DIR;
    case S_IFIFO:  return DT_FIFO;
    case S_IFLNK:  return DT_LNK;
    case S_IFREG:  return DT_REG;
    case S_IFSOCK: return DT_SOCK;
    default:       return DT_UNKNOWN;
    }
}

Un ejemplo más grande, contamos los archivos que hay en un árbol de directorios. Uno a uno, pero de forma recursiva (cuando encontramos un directorio nos meteremos dentro y llamaremos de nuevo a la función principal (cuentaArchivos()):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>

/* Función para devolver un error en caso de que ocurra */
void error(const char *s);

/* Calculamos el tamaño del archivo */
long fileSize(char *fname);

/* Sacamos el tipo de archivo haciendo un stat(), es como el stat de la línea de comandos */
unsigned char statFileType(char *fname);

/* Intenta sacar el tipo de archivo del ent */
unsigned char getFileType(char *ruta, struct dirent *ent);

/* Obtiene el nombre del fichero con la ruta completa */
char *getFullName(char *ruta, struct dirent *ent);

/* Genera una cadena de espacios, para dibujar el árbol */
char *generaPosStr(int niv);

/* Función principal, que cuenta archivos */
unsigned cuentaArchivos(char *ruta, int niv);

int main(int argc, char *argv[])
{
  unsigned num;

  if (argc != 2)
    {
      error("Uso: ./directorio_2 <ruta>\n");
    }
  printf("Entrando en: %s\n", argv[1]);
  num=cuentaArchivos(argv[1], 1);
  printf("%s . Total: %u archivos\n", argv[1], num);
  /* Empezaremos a leer en el directorio actual */

  return EXIT_SUCCESS;
}

void error(const char *s)
{
  /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */
  perror (s);
  exit(EXIT_FAILURE);
}

char *getFullName(char *ruta, struct dirent *ent)
{
  char *nombrecompleto;
  int tmp;

  tmp=strlen(ruta);
  nombrecompleto=malloc(tmp+strlen(ent->d_name)+2); /* Sumamos 2, por el \0 y la barra de directorios (/) no sabemos si falta */
  if (ruta[tmp-1]=='/')
    sprintf(nombrecompleto,"%s%s", ruta, ent->d_name);
  else
    sprintf(nombrecompleto,"%s/%s", ruta, ent->d_name);
 
  return nombrecompleto;
}

char *generaPosStr(int niv)
{
  int i;
  char *tmp=malloc(niv*2+1);    /* Dos espacios por nivel más terminador */
  for (i=0; i<niv*2; ++i)
    tmp[i]=' ';
  tmp[niv*2]='\0';
  return tmp;
}

unsigned cuentaArchivos(char *ruta, int niv)
{
  /* Con un puntero a DIR abriremos el directorio */
  DIR *dir;
  /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */
  struct dirent *ent;
  unsigned numfiles=0;          /* Ficheros en el directorio actual */
  unsigned char tipo;       /* Tipo: fichero /directorio/enlace/etc */
  char *nombrecompleto;     /* Nombre completo del fichero */
  char *posstr;         /* Cadena usada para posicionarnos horizontalmente */
  dir = opendir (ruta);

  /* Miramos que no haya error */
  if (dir == NULL)
    error("No puedo abrir el directorio");
 
  while ((ent = readdir (dir)) != NULL)
    {
      if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) )
    {
      nombrecompleto=getFullName(ruta, ent);
      tipo=getFileType(nombrecompleto, ent);
      if (tipo==DT_REG)
        {
          ++numfiles;
        }
      else if (tipo==DT_DIR)
        {
          posstr=generaPosStr(niv);
          printf("%sEntrando en: %s\n", posstr, nombrecompleto);         
          printf("%s%s . Total: %u archivos ", posstr, nombrecompleto, cuentaArchivos(nombrecompleto, niv+1));
          /* Podemos poner las líneas que queramos */
          printf("\n");
          free(posstr);
        }
      free(nombrecompleto);
    }
    }
  closedir (dir);
 
  return numfiles;
}

unsigned char getFileType(char *nombre, struct dirent *ent)
{
  unsigned char tipo;

  tipo=ent->d_type;
  if (tipo==DT_UNKNOWN)
    {
      tipo=statFileType(nombre);
    }

  return tipo;
}

/* stat() vale para mucho más, pero sólo queremos el tipo ahora */
unsigned char statFileType(char *fname)
{
  struct stat sdata;

  /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */
  if (stat(fname, &sdata)==-1)
    {
      return DT_UNKNOWN;
    }

  switch (sdata.st_mode & S_IFMT)
    {
    case S_IFBLK:  return DT_BLK;
    case S_IFCHR:  return DT_CHR;
    case S_IFDIR:  return DT_DIR;
    case S_IFIFO:  return DT_FIFO;
    case S_IFLNK:  return DT_LNK;
    case S_IFREG:  return DT_REG;
    case S_IFSOCK: return DT_SOCK;
    default:       return DT_UNKNOWN;
    }
}

Se puede descargar todo el código fuente desde aquí: Listado de archivos dentro de un directorio en C (3.5Kb)

Actualización (14/10/2011): En el blog código para llevar encontramos un ejemplo de cómo utilizar este código para encontrar el número de núcleos de nuestra CPU.

Guía de supervivencia para disfrutar de Linux Mint Debian Edition XFCE (III : ssh, nfs, y algunas utilidades para consola)

Jueves, 22 de Septiembre de 2011 Gaspar Fernández Sin comentarios

Cosas que no vienen instaladas de serie y deberían, herramientas para redes y administración remota que nos permitirán, sobre todo jugar y aprender. Quiero que esta sea una guía completa para personas que se inician en el mundo Linux por lo que no profundizaré mucho en algunos aspectos y en ocasiones, como esta, serán cosas muy básicas.

Servidor SSH

Cuando tienes en casa varios ordenadores, en ocasiones tenemos la necesidad de acceder a uno de los otros ordenadores y ejecutar algo allí, ya sea porque tengamos archivos en ese ordenador que no tengamos en el que tenemos delante, porque vamos a realizar algo pesado y la otra máquina es más larga, o bien porque no tenemos acceso físico a dicha máquina.

Lo primero es instalar el servidor SSH en la máquina a la que queremos acceder:

$ sudo apt-get install openssh_server

El cliente debemos instalarlo en la máquina desde la que accedemos

$ sudo apt-get install openssh_client

Aunque dado lo poco que ocupan, podemos, sin peligro instalar los dos en nuestro ordenador… nunca se sabe cuándo podemos utilizarlo.

La configuración que viene por defecto es buena para empezar. Ahora, si estamos en la máquina A y queremos conectar a la máquina B, podemos hacer desde un terminal:

$ ssh usuario@DIRECCION_IP_DE_B

y podremos ejecutar comandos en la máquina B

Compartir directorios por NFS

Imagina que estoy en la máquina A y toda mi colección de música está en la máquina B, si quiero reproducirla tendré dos opciones: grabarla en un sopore extraíble, lo cual requiere acceso físico (y tener un soporte para copiar), o compartirla por red local. Para esta segunda tenemos el protocolo NFS.

En la máquina B, la que tiene toda la música, debemos instalar el servidor:

$ sudo apt-get install nfs-server

y en la máquina A, el cliente:

$ sudo apt-get install nfs-client

Ahora debemos configurar qué queremos compartir. En muchos entornos tenemos aplicaciones para configurar los directorios compartidos de forma gráfica, pero para los que no, debemos abrir un terminal y ejecutar:

$ editor /etc/exports

# editor carga el editor de texto por defecto… os gusta nano ? vim ? emacs ? joe?

Dentro del archivo tenemos que introducir el directorio a compartir y el host con el que lo compartimos. Si somos la mágina B:

/directorio/a/compartir      DIRECCION_DE_A(ro,sync,no_subtree_check)

A partir de ahí podemos incluir tantos directorios compartidos como queramos, siempre en el mismo formato. He colocado unos atributos por defecto: ro,sync,no_subtree_check, podemos (y a veces debemos) cambiarlos:

  • ro - Comparte el recurso como sólo lectura. Si queremos compartirlo tanto para lectura como para escritura, debemos poner rw (read/write).
  • sync - Sincroniza las escrituras enviando notificaciones al cliente cuando se escriben para que pueda continuar. Podemos poner async, y en este caso no se envían notificaciones y las escrituras son más rápidas.
  • no_subtree_check. No verifica los permisos en directorios anteriores a la ruta especificada, puede que el usuario ptolomeo no tenga acceso a /home/pitagoras/ pero sí pueda acceder a /home/pitagoras/compartido ; si esta opción es subtree_check, provocaríamos que ptolomeo no acceda a /home/pitagoras/compartido porque no tiene acceso al directorio anterior.

Ahora vamos a la parte del cliente, éste tiene que montar los recursos compartidos. Imaginémonos que el usuario ptolomeo quiere montar en /home/ptolomeo/amigos/pitagoras el recurso compartido que éste tiene en /home/pitagoras/compartido. Ptolomeo deberá hacer:

$ sudo mount -t nfs IP_DEL_ORDENADOR_DE_PITAGORAS:/home/pitagoras/compartido /home/ptolomeo/amigos/pitagoras

Por otra parte, si tenemos un servidor NFS y queremos ver qué recursos tiene compartidos, podemos verlo haciendo:

$ sudo showmount -e IP_DEL_SERVIDOR

La seguridad ante todo

Está bien tener un servidor ssh y nfs ; pero en ocasiones, nos conectamos a una red pública, o directamente a Internet, por lo que no nos interesa compartir nuestros archivos con cualquiera que pueda entrar, para ello tenemos los archivos hosts.deny (que deniega el acceso a nuestros servicios a diferentes máquinas) y hosts.allow (que da permiso para que dichas máquinas accedan a nuestro ordenador).

Ambos archivos funcionan de forma parecida, escribimos en una línea:

SERVICIO : IP/HOST1, IP/HOST2, IP/HOST3, ….

donde SERVICIO, puede ser, por ejemplo ssh, luego ponemos dos puntos (:) y una lista de IP o nombres de host separados por comas. Dependiendo de si esa línea la ponemos en hosts.deny o hosts.allow, denegaremos o permitiremos el acceso a esa máquina.

Para hacer una configuración segura, en hosts.deny podemos poner:

ALL : ALL

Lo que bloqueará TODOS los servicios para TODOS los ordenadores remotos, así tendremos nuestro ordenador cerrado al exterior. Ahora, en hosts.allow, queremos dar permiso a ptolomeo para que acceda a nuestro servicio ssh:

sshd : ptolomeo

también queremos dar permiso al ordenador con IP 192.168.1.28, al ordenador pitagoras y a 192.168.1.20 para acceder al servicio httpd (servidor web, imaginemos que lo tenemos instalado):

httpd: 192.168.1.28, pitagoras, 192.168.1.20

Por otra parte, el servicio nfs, hemos visto que compartimos directorios con máquinas determinadas, por lo que así podríamos restringirlo, además, si en hosts.deny ponemos ALL:ALL no va a arrancar el demonio; tenemos dos posibles soluciones:

  1. Poner en hosts.deny
    ALL : ALL EXCEPT LOCAL
    Para que no se bloqueen conexiones locales
  2. Poner en hosts.allow
    rpcbind : localhost
    Para no bloquear conexión local al demonio rpcbind

Si queremos permitirlo todo, también podemos poner en hosts.allow:

ALL : ALL

pero no se recomienda, sólo debemos hacer esto para equipos de pruebas o máquinas que tengan una conexión limitada (es decir, que sólo estén conectadas con uno o dos hosts y esté todo controlado).

Instalar nmap

Este programa tiene muchas utilidades, una de las más comunes es saber qué servicios tiene abiertos una máquina, o por ejemplo, nuestro router tiene un servidor DHCP para darnos una dirección IP en nuestra red (es la configuración más común), cada vez que encendamos nuestro ordenador, lo más probable es que tengamos una IP diferente asignada, y si hay varios ordenadores conectados, ya sí que es difícil saber la IP que tiene cada máquina, nmap nos puede ayudar a eso.

Además, podemos utilizar nmap para averiguar si hay intrusos en nuestra red.

$ sudo apt-get install nmap

Si queremos ver cuánta gente hay conectada en nuestra red, y quiénes son los que están conectados, podemos hacer lo siguiente (si nuestra IP es, por ejemplo 192.168.1.10:

$ nmap 192.168.1.*

Al cabo de unos segundos nos dirá qué máquinas hay conectadas (esto es muy útil si conectamos a través de la red de nuestra facultad y queremos saber lo archivos y servicios que tienen compartidos ver quién está conectado.

Instalar pmount

Yo soy un usuario más de consola, y a veces tengo varios equipos en red local y en diferentes habitaciones. Cuando no tienes entorno gráfico no hay icono para hacer click con el derecho y montarlo. Así que tenemos dos posibilidades, en el caso de querer montar un dispositivo usb flash que está en /dev/sdc1:

$ sudo mkdir /media/pendrive

$ mount /dev/sdc1 /media/pendrive

O utilizar pmount, es más se puede utilizar como un usuario normal (siempre que esté en el grupo plugdev):

$ pmount /dev/sdc1

Automáticamente creará el punto de montaje, que se borrará cuando desmontemos con:

$ pumount /dev/sdc1

Por lo tanto, para instalar este pequeño pero útil programa:

$ sudo apt-get install pmount

$ sudo gpasswd -a USUARIO plugdev

La última línea corresponde a la inclusión del usuario USUARIO en el grupo plugdev para poder montar los dispositivos.

Generador de contraseñas en consola

Una gran utilidad que uso a menudo es un generador de contraseñas y es que, para tener seguridad, es conveniente tener una contraseña para cada servicio/web/cosa que tengamos en el sistema, e incluso si se está creando una base de datos, con un usuario que tenga acceso a ella (sobre todo si no está en nuestro ordenador), es conveniente darle una contraseña “aleatoria”, o al menos difícil de sacar. Para ello tenemos pwgen:

$ sudo apt-get install pwgen

$ pwgen

So9eejei Eej9youf Iex0ga3y rooV0aRi loXi8ooD Eilah4ru eeD7iego ieX6eth9
ohz9Que9 eiJohz5i ku9doo7E Ooghie6U zee9TeiB phei0eeG ith3Aex4 coiYa0ca
mah3aPae YahxaiM9 quiqu9Oi Voh9olub xanoo1Ae ohn8Ahng aiyoh6eG Coo3quok
eiXou6Zi Eis2poo1 bu1iBahf Aey0eeng aethee3K Thoh2ood Aepai6ph iRah2awo
eePei8si tahf5Pe0 Aegh0phu AeR0eech quohce3A siSh8Hit eeQuu4Ei ahGhal5f
Kahgh9ah aJoh7ex6 teeF4Eil Eehue3ee ooca8EiT huzaev8P eiK2quoh muuzae6O
aj6Mu3Ae UPheew2o deiHee8N abeesh4M thei8ohZ Ei3Aingo Phie7eep av0koo5U
koh2Jahb caiv1Cho Oa6fohvu OochaeW9 TeeN2Lee ahlaeH8o sad9Aegh Taiz6gae
looF2mie jahjoaS4 laineiN9 ush1oa0I ipob2Ohs Ha1ohjai OoL5wa7u Eipohva7
aimoox9K Keey4ieW labah4Ci oowaiJ2i pahxaiB1 eiWai3ei bethaF0r fu8eiV7u
Wo3Te1iu roeZ3eil Oorohs8k cah7ESo9 Yohg1aix ohz9Choo Ogooc1ph Eh7eeghi
eeh8Aer2 eph4Ehoh Eis0Ieva Yeochoh9 Aizahn4i eepahH6o wahBae5o eeZiang6
aep0ahPh haewoh7B aidoh9Ea ia7tai8L Tohx5gae OSeexee2 ooN6liWo Pai8Ohs0
ZeiX6iay zie3Iexe Oog2caek Ohoo9eit yoo4eiSe bai0Ooth ieHa5Aig Xie8eeph
Uasoov2a Iesh6EiR ne1Aothi oo7oob7O Lohy5wai zea1Xie3 ooH3be9i fe7Ohnoo
ooSosae2 phei3AeB aerie9Ai ku9ohBoh peegieX5 Eiph0ool choQuoo3 Reew9Ohp
Kohm3aXi Kui9ohCh reifieM0 sha3Yaos emohFo2i ca5zaeP5 Mu4vohgh naiV8Chi
ue7Pooso eePahth6 waLuu7oh Yee8teir eth9Tai8 cho7Poog ooSee6yu Uquai6aa
uqu8Li2u eegooL0i ieth9Aem Miex4Nah ii5Eephi iChae5do fahX9foh eMaexu8i
koh2Ca7J Theem9co eiH4zev4 chofeW9f doh5eK8f ori7ohV5 zo4zohNg saeKah9N

Nada más ejecutarlo nos dará muchas contraseñas para elegir. E incluso podemos personalizar la salida con algunos modificadores (-s para incrementar la seguridad; -y para incluir símbolos; y algunos más)

Links, un navegador web en modo texto

Y el usuario de a pie se preguntará: “¿para qué quiero yo un navegador web en modo texto?”, y es cierto, muchos de nosotros arrancamos el navegador y vemos las imágenes y ejecutamos scripts y “disfrutamos” de la experiencia web; aunque en ocasiones nos puede sacar de algún apuro o por ejemplo, hace muy fácil la creación de scripts que interactúen con un componente web. Por ejemplo:

$ links -dump http://checkip.dyndns.org

Current IP Address: 94.56.107.7

Con lo que nos conectamos a la web http://checkip.dyndns.org para saber nuestra dirección IP (con la que conectamos a Internet). O por ejemplo, si queremos ver la fecha y hora actual descargadas de un servidor de Internet:

$ links -dump  http://www.timeanddate.com/worldclock/city.html?n=141  | grep Current | sed ‘2!d’

Para instalar este programa:

$ sudo apt-get install links

Limitar el ancho de banda de nuestros servicios

Imagina que estamos subiendo un archivo a un FTP de un amigo, descargando nuestra distribución de Linux preferida, enviando imágenes de nuestra webcam a Internet, además, mientras estás escuchando la radio o viendo la tele por Internet, nuestros recursos son limitados y si tienes una conexión de 10Mbps  de bajada y 500Kbps de subida, aparte de dar las gracias, tienes que repartirla sabiamente. Tal vez el FTP de nuestro amigo se va a coger todo el ancho de banda de subida que tengamos, porque puede, y luego las imágenes de la webcam, a lo mejor no se suben al mismo ritmo; por otra parte, la distribución de Linux irá muy rápido, pero a lo mejor se resiente el canal de TV online que estamos viendo. Para eso tenemos trickle, una herramienta capaz de limitar el ancho de banda de red que consume una aplicación. Podemos limitar el ancho de banda de la descarga de nuestra distribución Linux:

$ trickle -d 100 wget http://mirror.linuxmint-fr.net/linuxmint.com/stable/debian/linuxmint-xfce-201104-dvd-32bit.iso

Para limitar la descarga a 100Kb/s

Para instalar:

$ sudo apt-get install trickle

Ver tareas en ejecución en colores desde consola

LMDE GnomeA veces, para ver los procesos en ejecución, top, no es suficiente. No nos deja ver todos los procesos, y a veces la información es algo escasa, para eso tenemos htop, podemos ver una captura de la ejecución de htop en una máquina remota con LMDE Gnome.

$ sudo apt-get install htop

Para terminar

Hay muchas más aplicaciones muy útiles para consola, estas son algunas, una mínima muestra de toda la potencia que encierra nuestra máquina Linux y que no vienen instaladas de serie en Linux Mint Debian Edition; aunque algunas de ellas sí que vienen en otras distribuciones.

Guía de supervivencia para disfrutar de Linux Mint Debian Edition (II : Synaptics / ipw2200/cambios menores/driver NVIDIA/SMP)

Jueves, 15 de Septiembre de 2011 Gaspar Fernández Sin comentarios

Esta segunda entrega puede que a muchos no os interese mucho. Sobre todo trataré el tema de Synaptics, no confundir con el gestor de paquetes synaptic. Yo me refiero al controlador del touchpad de mi portatil. Es un touchpad algo antiguo (no multitouch), y es que, LMDE, por defecto no permite hacer click golpeando con suavidad el touchpad, ni permite hacer scroll deslizando el dedo por la parte derecha… por eso, vamos a cambiar un poco la configuración.

Configuración cómoda para el panel Synaptics

En esta versión de Xorg, que ya no se apoya en HAL para detectar dispositivos, podemos añadir múltiples ficheros de configuración.  La configuración inicial se encuentra en /usr/share/X11/xorg.conf.d/, aunque no es recomendable tocar ahí, ya que cualquier actualización podría borrar del disco nuestro trabajo; para ello, tenemos que crear el directorio /etc/X11/xorg.conf.d/ y meter ahí todos los ficheros de configuración para las X que queramos.

Crearemos pues, el archivo /etc/X11/xorg.conf.d/50-synaptics.conf (el 50 es sólo un número que establecerá el orden de carga, nada más):

Section "InputClass"
        Identifier                     "touchpad catchall"
        Driver                         "synaptics"
        MatchIsTouchpad                "on"
        Option  "Protocol"             "auto-dev"
        Option  "LeftEdge"             "1700"
        Option  "TapButton1"           "1"
        Option  "TapButton2"           "2"
        Option  "TapButton3"           "3"
        Option  "RightEdge"            "5300"
        Option  "TopEdge"              "1700"
        Option  "BottomEdge"           "4200"
        Option  "FingerLow"            "25"
        Option  "FingerHigh"           "30"
        Option  "MaxTapTime"           "180"
        Option  "MaxTapMove"           "220"
        Option  "VertScrollDelta"      "100"
        Option  "MinSpeed"             "0.66"
        Option  "MaxSpeed"             "0.92"
        Option  "AccelFactor"          "0.0010"
        Option  "SHMConfig"            "off"
        Option  "VertTwoFingerScroll"  "1"
        Option  "HorizTwoFingerScroll" "1"
        Option  "VertEdgeScroll"       "1"
        Option  "HorizEdgeScroll"      "1"
        MatchDevicePath "/dev/input/event*"
EndSection

Muchos valores son descargados de Internet, de varios foros, y otros son gracias a la investigación y tanteo. Aquí una explicación rápida de cada opción:

  • LeftEgde, RightEdge, BottomEdge, TopEdge, son los bordes, podemos jugar con estos valores para dar y quitar resolución al panel táctil.
  • FingerHigh es la presión necesaria para que el driver considere que hemos tocado el panel y FingerLow es la presión necesaria para que se considere soltado el panel.
  • MaxTapTime es el tiempo máximo de un toque al panel.
  • MaxTapMove es el movimiento máximo del dedo para que se detecte toque y no movimiento
  • VertScrollDelta, HorizScrollDelta, es lo que se tiene que mover el dedo para detectar un desplazamiento vertical u horizontal
  • MinSpeed, MaxSpeed, la máxima y mínima velocidad
  • AccelFactor, factor de aceleración
  • SHMConfig, permite configuración desde un cliente externo, además es una salida de depuración del panel synaptics.
  • VertTwoFingerScroll, HorizTwoFingerScroll, desplaza vertical y horizontalmente con dos dedos (en cualquier parte del panel)
  • VertEdgeScroll, HorizEdgeScroll, activa el desplazamiento en el borde del panel.

Para muchas más opciones, podéis consultar:

man synaptics

Arreglar desconexiones aleatorias del driver ipw2200

Los que tengáis este driver wifi lo sabréis, cuando llevas un rato conectado a una red, y no estamos haciendo nada, se cierra la conexión y perdemos la red inalámbrica. Esto es un fallo por parte de Intel, y lo encontramos en todas las distribuciones. Y es que es hardware antiguo y ya no tiene soporte apenas. El problema está en el firmware, y es que todas las distribuciones instalan la versión 3.1 cuando ésta es la que tiene el gran fallo. Volviendo a la versión 3.0 todo volverá a ir bien.

Para ello:

$ sudo apt-get remove firmware-ipw2×00

Ahora descargamos de aquí la versión 3.0 del firmware, la descomprimimos en un directorio temporal y la copiamos en /lib/firmware:

$ mkdir tmp
$ cd tmp
$ tar xvzf ipw2200-fw-3.0.tgz
$ cp ipw2200-fw-3.0/* /lib/firmware

Debería funcionar mucho mejor ahora.

Cambios menores: bug de evdev

Si por algún casual, al reiniciar el ordenador, no puedes mover ni el teclado, ni el ratón, consulta este artículo.

Cambios menores: recorrer el historial de bash con Av-pag y Re-pag

Si te gustaría tener esta característica (la cual es muy interesante para escribir mucho más rápido), consulta este artículo

Cambios menores: desactivar el altavoz interno

Este altavoz interno produce un pitido muy molesto cada vez que estamos en el terminal y nos equivocamos o damos un par de veces al tabulador para ver las posibles opciones.

Para ello editamos el archivo /etc/modprobe.d/blacklist.conf y añadimos esta línea:

blacklist pcspkr

Cambios menores: devolver la búsqueda por defecto de Google a Firefox

Apoyo LinuxMint, aunque me gusta la búsqueda tradicional de Google, y con el tema de LinuxMint pierde muchísimo, hay miles de cosas que ya no puedo hacer cuando busco. Por eso, tendremos que cambiar esto también.

Para ello, debemos descargar el fichero google.xml desde aquí y lo debemos copiar a /usr/share/linuxmint/adjustments/firefox y en /opt/firefox/searchplugins:

$ wget http://mxr.mozilla.org/firefox/source/browser/locales/en-US/searchplugins/google.xml?raw=1
$ sudo cp google.xml /usr/share/linuxmint/adjustments/firefox/
$ sudo cp google.xml /opt/firefox/searchplugins/

# Si ya teníamos instalado Linux Mint, o hemos accedido a Firefox (y por ello se ha creado un perfil), debemos también borrar search.json del directorio de nuestro perfil de Firefox (esto es un ejemplo):

$ rm ~/.mozilla/firefox/1q356xaz.default/search.json

Reiniciamos Firefox y ya está.

El audio no funciona bien

Si tienes problemas con el sonido, puede deberse a muchas cosas, normalmente la configuración que viene de fábrica es buena, pero a veces hay problemas en este sentido con algún driver que no funciona del todo bien o con algún programa que no se lleva bien con los demás.

Por ello, mi primera recomendación es instalar pavucontrol, es un control de volumen gráfico para pulseaudio, que nos permite saber qué aplicaciones están reproduciendo sonido y por qué dispositivos físicos (puede que no sea un dispositivo físico el que esté sonando), también nos permite silenciar aplicaciones y alguna cosa más.

$ sudo apt-get install pavucontrol

Si no has conseguido hacer que el sistema suene, lo mejor es visitar el foro de Linux Mint donde dan muchas pistas sobre la configuración de pulseaudio y de los módulos controladores del dispositivo. Aquí lo encontramos.

Instalar controlador binario para tarjetas NVIDIA

El controlador que viene por defecto, nouveau, no da el rendimiento máximo para tareas en 3D por ejemplo, si queremos un escritorio con efectos y todo, por lo que tendremos que instalar el controlador oficial, para ello, primero, tendremos que eliminar algunas cosas:

$ sudo apt-get remove –purge xserver-xorg-video-nouveau xserver-xorg-video-nv

Ahora tenemos que instalar el controlador de NVIDIA:

$ sudo apt-get install xserver-xorg-video-nvidia

Y tras ello, hacer que el servidor X coja el driver, para ello creamos un archivo en /etc/X11/xorg.conf.d/ que se llame por ejemplo nvidia.conf y contenga lo siguiente:

Section "Device"
    Identifier "Graficos"
    Driver     "nvidia"
EndSection

Salvamos, reiniciamos, y ya estamos utilizando el driver oficial de NVIDIA.

Atención usuarios de MultiCore!

Si habéis instalado Linux Mint Debian Edition en un ordenador cuya CPU tenga más de un núcleo, tal vez habréis visto que sólo funciona un núcleo, y es que el núcleo que viene por defecto (en la versión 32bit) sólo soporta un núcleo (herencia de Debian, y desde abril de 2011).

Lo podemos ver  desde consola haciendo:

$ cat /proc/cpuinfo

Donde veremos sólo información de una CPU. O también:

$ inxi -C

CPU:       Single core Intel Atom D510 (-UP-) cache 512 KB flags (lm nx sse sse2 sse3 ssse3) clocked at 1662.501 MHz

Donde veremos una respuesta parecida a la anterior. Aunque si profundizamos un poco, podremos ver:

$ dmesg | grep CPU

[ 0.000000] ACPI: NR_CPUS/possible_cpus limit of 1 reached. Processor 1/0×2 ignored.
[ 0.000000] ACPI: NR_CPUS/possible_cpus limit of 1 reached. Processor 2/0×1 ignored.
[ 0.000000] ACPI: NR_CPUS/possible_cpus limit of 1 reached. Processor 3/0×3 ignored.

¿ Cómo que se ignoran los procesadores ? ¿El número de cpus posible es 1 ?

Lo que tenemos que hacer es instalar otra versión del núcleo que sí soporte multiprocesador. Para ello, debemos hacer lo siguiente:

$ sudo apt-get installlinux-headers-2.6-686-pae  linux-image-2.6-686-pae

Al instalar la versión para 686 en lugar de la versión para 486 (que viene por defecto), daremos soporte también para multi-procesador. Ahora veremos:

$ inxi -C

CPU:       Dual core Intel Atom D510 (-HT-MCP-) cache 512 KB flags (lm nx sse sse2 sse3 ssse3)
Clock Speeds: (1) 1662.501 MHz (2) 1662.501 MHz (3) 1662.501 MHz (4) 1662.501 MHz

Solución temporal al problema con udev 171

Jueves, 8 de Septiembre de 2011 Gaspar Fernández Sin comentarios

Desde Mayo de 2011 tenemos a nuestra disposición una de las versiones con más bugs de udev, este gestor de dispositivos de Linux. En concreto, el bug del que hablo está apareciendo desde Julio/Agosto de este año. La forma de reproducirlo es muy fácil, tenemos que tener el servidor X con la configuración automática (en muchas distribuciones) o utilizando dispositivos de eventos, desde el arranque, ni el teclado ni el ratón funcionan.

tecladosLa primera vez que me pasó pensé en el servidor X, de hecho, como no funcionaba el teclado, no se podía cambiar a consola con Ctrl+Alt+Fn (n=1,2,3,4,….) ; por lo que fue necesario pulsar Alt+Sysrq+R antes de poder cambiar a la consola con Alt+Fn.

Dicho esto, había que trastear un poco, y es que la culpa no es de las X, éstas estaban bien configuradas, aprovechando que utilizo una de las últimas versiones, está configurada para no utilizar HAL a la hora de detectar dispositivos de entrada, y apoyarse directamente en lo que udev le dice.

Si el sistema está recién instalado, podemos ver que la configuración sí que funcionaba perfectamente nada más arrancar, es a la segunda vez cuando ya no hace las cosas bien, esto nos da una pista de que puede haber algún archivo que se escribe y puede estar metiendo la pata… ¡ lo borramos y ya está ! Aunque tendremos que hacerlo cada vez que arrancamos el ordenador. Por otra parte, si enchufamos el teclado o ratón de nuevas, el sistema hace las cosas bien, es decir, podemos desenchufar y enchufar el ratón y no pasaría nada (sería un “problema menor” aunque incómodo), lo malo es si trabajamos desde un portátil.

Dado que la versión 171 de udev es una de las más actuales, y que para la 172 (aún experimental) hay que instalar muchos otros componentes también experimentales, no estaría mal aplicar un pequeño parche antes de actualizar nuestro sistema con componentes que a lo mejor luego nos dan algún problema, además de que para muchas distribuciones aún no están disponibles los paquetes.

Podemos optar por varias soluciones:

Eliminar archivos al apagar el ordenador automáticamente

Consiste en hacer rm -rf /var/run/udev al apagar el ordenador (más adelante pondré un ejemplo parecido, junto con otra solución).

Configurar el servidor X a la antigua usanza

Es decir, configurarlo con la ubicación exacta de los dispositivo. En un portátil no pasa nada, casi siempre vamos a tener los dispositivos en el mismo orden (el teclado, el touchpad, etc), aunque no seremos tan “modernos”, y puede que alguna vez sí que tengamos algún problema si se nos ocurre enchufar un teclado externo o varios dispositivos apuntadores o algo así, de esas veces que a udev se le ocurre asignar nombres diferentes a los dispositivos (eso, cuando todo funciona, no es un problema.

La forma de configurar los dispositivos a la antigua usanza es la siguiente (modificanfo /etc/X11/xorg.conf):

Section "InputDevice"
    Identifier     "Configured Mouse"
    Driver         "evdev"
    Option         "CorePointer"
    Option         "Device" "/dev/input/event1"
EndSection

Section "InputDevice"
    Identifier     "Configured Keyboard"
    Driver         "evdev"
    Option         "CoreKeyboard"
    Option         "Device" "/dev/input/event0"
EndSection

Suponiendo que el teclado está en /dev/input/event0 y el ratón (o touchpad) en /dev/input/event1 . Una forma de averiguarlo rápida y sencilla (ya que evdev no funciona bien) es hacer un cat…

$ ls /dev/input/
by-id  by-path  event0  event1  event2  event3  event4  mice  mouse0
$ cat /dev/input/event0

# Tocar las teclas Control, Mayúscula, Alt y alguna de esas que no imprimen caracteres y ver si sale algo por pantalla. Si vemos que sí, ese dispositivo será nuestro teclado. Si movemos el ratón y sale algo, ese dispositivo será el ratón.

# Control +C para salir, puede que cuando terminemos tengamos que hacer reset en la terminal.

$ cat /dev/input/event1

$ reset

Para saber más acerca de configurar los dispositivos para Xorg de esta forma, se pueden visitar las wikis de Gentoo o de Arch Linux, por poner dos ejemplos.

Desactivar AutoAddDevices en Xorg

Lo podemos hacer añadiendo en /etc/X11/xorg.conf las siguentes líneas:

Section "ServerFlags"
   Option "AutoAddDevices" "false"
EndSection

Esto hará que no se usen los drivers de dispositivos de eventos, sino los tradicionales “kbd” y “mouse”, y en algunas distribuciones tendremos que instalarlos a mano, ya que no vienen.

Hacer como que desenchufamos y enchufamos los dispositivos

Y es que en Linux podemos hacerlo de forma muy sencilla, sólo tenemos que descargar un módulo y cargarlo de nuevo. No me extenderé mucho en la explicación, ya que ésta llegará pronto a este blog, e incluso algo más extendida.

Lo malo es que este método está particularizado para los Debian-like (Debian/*Ubuntu/Linux Mint y otros 1000 más, aunque casi casi al 100% funcionarán en otras distribuciones.

Lo primero que tenemos que hacer es añadir el módulo del kernel evdev (sí, al menos tenemos que tenerlo como módulo, si no, debemos elegir una de las otras formas), para ello editamos /etc/modprobe.d/blacklist.conf y añadimos:

blacklist evdev

ahora lo que debemos hacer es cargar automáticamente el módulo de nuevo, así forzamos a udev a releer los nuevos dispositivos /dev/input/event* que se crearán. Para ello, creamos un script de arranque de esos que vemos en /etc/init.d/… y esto es lo que está particularizado para los Debian Like, haré un copia y pega de mi script, ya lo comentaré en profundidad más adelante. llamémoslo evdev_load. NOTA: los comentarios son necesarios :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/sh
#
### BEGIN INIT INFO
# Provides:     evdev_load
# Required-Start:       $dbus
# Required-Stop:
# Default-Start:        2
# Default-Stop:
# X-Interactive:        true
# Short-Description:    Carga el evdev en el arranque
### END INIT INFO

case "$1" in
        start)
                echo "Cargando evdev"
                modprobe evdev
        ;;
esac

Una vez hecho esto, terminamos el proceso de la siguiente manera:

$ sudo update-rc.d evdev_load start 99 2

para que arranque cada vez que arranquemos el sistema.

Foto: JohnJMatlock

Visita otras webs de la red