Archivo

Archivo del autor

Hallar la IP de un dispositivo en C

Lunes, 30 de Abril de 2012 Gaspar Fernández 2 comentarios

Puede que se nos haya presentado alguna vez la necesidad de saber la dirección de un dispositivo desde nuestro programa, y no es plan de ponernos a ejecutar ifconfig o algún programa parecido para hallar la dirección.

Podemos hablar con ioctl() una función destinada a definir y obtener información de dispositivos. Hay cientos de llamadas, aquí comentaremos la llamada SIOCGIFADDR cuya función es la que comentábamos, obtener la dirección de un interfaz de red.

Podemos probar el siguiente programa con el que hallaremos la IP del dispositivo eth0:

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
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>

int main()
{
  int sock;
  struct ifreq ifr;

  sock=socket(AF_INET, SOCK_DGRAM, 0);
  if (sock<0)
    return -1;          /* No puedo crear el socket */

  ifr.ifr_addr.sa_family = AF_INET;
  strcpy(ifr.ifr_name, "eth0");

  if (ioctl(sock, SIOCGIFADDR, &ifr) < 0)
    return -1;          /* No encuentro el dispositivo */
 
  printf("IP de eth0: %s\n", inet_ntoa( (*(struct in_addr *) &ifr.ifr_addr.sa_data[2])));

  close(sock);

  return 0;
}

Primero creamos un socket, luego definimos la familia y el dispositivo que queremos consultar con:

1
2
ifr.ifr_addr.sa_family = AF_INET;
  strcpy(ifr.ifr_name, "eth0");

Para comprender cómo obtenemos la dirección debemos echar un ojo a las estructuras:
(struct ifreq)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct ifreq
{
    char            ifr_name[IFNAMSIZ];   /* Nombre de la interfaz */
    union {
                    struct sockaddr       ifr_addr;
                    struct sockaddr       ifr_dstaddr;
                    struct sockaddr       ifr_broadaddr;
                    struct sockaddr       ifr_netmask;
                    struct sockaddr       ifr_hwaddr;
                    short                 ifr_flags;
                    int                   ifr_ifindex;
                    int                   ifr_metric;
                    int                   ifr_mtu;
                    struct ifmap          ifr_map;
                    char                  ifr_slave[IFNAMSIZ];
                    char                  ifr_newname[IFNAMSIZ];
                    char *                ifr_data;
    };
}

(struct sockaddr)

1
2
3
4
5
struct sockaddr
{
   unsigned short sa_family;  /* familia de la dirección */
   char sa_data[14];          /* 14 bytes de la dirección del protocolo */  
};

(struct sockaddr_in)

1
2
3
4
5
6
7
struct sockaddr_in
{
   short int sin_family;        /* Familia de la Dirección              */
   unsigned short int sin_port; /* Puerto                               */
   struct in_addr sin_addr;     /* Dirección de Internet                */
   unsigned char sin_zero[8];   /* Del mismo tamaño que struct sockaddr */
};

(struct in_addr)

1
2
3
4
struct in_addr
{
   unsigned   long s_addr;
};

Bien, en el programa anterior, la línea más conflictiva es:

1
printf("IP de eth0: %s\n", inet_ntoa( (*(struct in_addr *) &ifr.ifr_addr.sa_data[2])));

En ella, sacaremos la dirección en forma de struct in_addr que es el que le gusta a inet_ntoa() (esta función sólo coge la dirección y la pone en forma de char* para que podamos entenderla.
Para llegar al struct in_addr, partimos de la estructura struct ifreq (variable ifr) que nos devuelve ioctl(), ésta tiene un campo llamado ifr_addr de tipo struct sockaddr. El tipo struct sockaddr y struct sockaddr_in podemos compararlos, es decir, podemos ponerlos uno encima de otro y pueden tener la misma información en el mismo byte, por tanto, podemos ver que el campo sa_data de un struct sockaddr coincide con sin_port, sin_addr y sin_zero de un struct sockaddr in; vemos que sin_port es de tipo unsigned short por tanto ocupará 2 bytes y como queremos la información encerrada en sa_data a partir del byte 2, de ahí viene dicho número. Si queremos ponerlo de otra forma, ésta podría ser:

1
2
struct sockaddr_in sain;
  printf("IP de eth0: %s\n", inet_ntoa( (*(struct in_addr *) &ifr.ifr_addr.sa_data[sizeof(sain.sin_port)])));

Linux on Movies: KDE en Miénteme

Jueves, 12 de Abril de 2012 Gaspar Fernández Sin comentarios

Una serie donde analizan micro-expresiones faciales y corporales para pillar a los malos. Miénteme. Analizan al milímetro los gestos de la gente y por supuesto, leen su correo en Kmail, aunque, como tapadera (ya que son todos unos expertos informáticos) reciben los e-mails a través de la lista de correo KDE-Plasma:
kde_mienteme
En el minuto 9:15 del capítulo 9 de la segunda temporada podemos ver este pantallazo durante un momento

Algoritmos: generar números aleatorios para la lotería

Lunes, 9 de Abril de 2012 Gaspar Fernández Sin comentarios

Es un ejemplo típico y nos muestra el uso de rand() con arrays para generar varios números aleatorios y no repetidos.

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
/**
*************************************************************
* @file loteria.c
* @brief Saca números aleatorios para la lotería
* Basado en el sorteo de la primitiva, hay que sacar 7 números
* del 1 al 49, sin repetir
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <time.h>       /* para time() */


int numero_aleatorio(int desde, int hasta)
{
  return desde+rand()%(hasta-desde+1);
}

short numero_repetido(int numeros[7], int n)
{
  int i=0;

  while (i<n)
    {
      /* Si un número sacado anteriormente es igual al número
         en la posición n, decidido, está repetido. */

      if (numeros[i]==numeros[n])
    return 1;
      i++;
    }

  /* Si llegamos hasta aquí, el número no se ha repetido */
  return 0;
}

void numeros_loteria(int numeros[7])
{
  int i;

  /* El primer número lo generamos, este no se repetirá con nadie
     anterior */

  numeros[0]=numero_aleatorio(1, 49);

  /* A partir del segundo número tenemos que verificar que no se
     repite. */

  for (i=1; i<7; i++)
    {
      do
    {
      /* Generamos un número */
      numeros[i]=numero_aleatorio(1,49);
      /* Si el número está repetido, volvemos a generar */
    } while (numero_repetido(numeros, i));
    }
}

int main(int argc, char *argv[])
{
  int numeros_premiados[7];
  int i;
  srand(time(NULL));        /* Una nueva semilla de números aleatorios */

  numeros_loteria(numeros_premiados);
  for (i=0; i<7; i++)
    printf ("Numero %d -> %d\n", i+1, numeros_premiados[i]);
 
  return EXIT_SUCCESS;
}

Descargar: loteria.c (1.6Kb)

En este ejemplo vamos guardando todos los números en un array y vamos comparando los nuevos números que vamos generando con los antiguos.

Linux on Movies: Person of Interest usa GNOME

Jueves, 5 de Abril de 2012 Gaspar Fernández 2 comentarios

En esta serie, Person of Interest (Vigilados en español) protagonizada por Jim Caviezel y Michael Emerson, salen escritorios Gnome 2 por todas partes, con diferentes temas.

Aunque no se termina de ver claro, hay algunas veces que se perfila algo, es un escritorio muy personalizado que siempre tiene textos de terminal o en hexadecimal en movimiento (porque los mejores informáticos lo hacen así)… pero en la barra inferior, tiene un texto “Applications”, “Places”, “System”, os suena… porque a mi me recuerda horrores a GNOME 2.

No dejo momentos concretos porque desde el primer capítulo se puede ver un escritorio así en el ordenador de uno de los protagonistas y escritorios parecidos en otros ordenadores que van apareciendo.

point_gnome
Por ejemplo esto es del episodio 9 de la primera temporada. Minuto 11:56 y esto del 31:28 (no hagáis mucho caso a las direcciones IP, porque parece que ese tema lo llevan un poco mal)
point_gnome2

Y este pantallazo del capítulo 13 de la primera temporada. Minuto 30:15 (aunque salen pantallas parecidas varias veces) me recuerda a Unity:
point_unity

Algoritmos: Validar un DNI en C

Miércoles, 4 de Abril de 2012 Gaspar Fernández Sin comentarios

En España, para identificar de forma única a cada ciudadano se utiliza el número del Documento Nacional de Identidad (DNI). Como es costumbre, en muchos datos numéricos, como este, un número de cuenta corriente, códigos ISBN, etc existe un algoritmo de verificación que comprueba que el número es válido. En este caso, la comprobación se realiza con la letra que acompaña el número.

El algoritmo es sencillo, puede que a la hora de implementarlo no nos acordemos del orden de las letras (para eso lo pongo, y podemos hacer Copia y Pega rápidamente).

Tal vez si estás empezando con la programación y no domines el uso de Arrays te resulte algo complicado, pero es un buen momento para empezar a programar Arrays, te ayudarán mucho y te ahorrarán en casos como este muchísimo código.

Presento primero una función que genera la letra del DNI a partir del número:

1
2
3
4
5
6
char letraDNI(int dni)
{
  char letra[] = "TRWAGMYFPDXBNJZSQVHLCKE";

  return letra[dni%23];
}

En definitiva, necesitamos averiguar el resto de el número del DNI entre 23 (lo cual nos dará una cifra entre 0 y 22), cada cifra corresponderá con una letra: el 0 con la T, el 1 con la R, el 2 con la W…

Podemos utilizar esta función, no para generar, sino para validar un DNI almacenado en una cadena de caracteres:

1
2
3
4
5
6
7
short verificaDNI(char *dni)
{
  if (strlen(dni)!=9)
      return 0;
  else
    return (letraDNI(atoi(dni))==dni[8]);
}

En ella, convertimos el número a entero (después de verificar que hay 9 caracteres), y se lo pasamos a letraDNI() para que genere una letra, luego comparamos la letra generada con la letra introducida por el usuario.

Aquí vemos el código completo:

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
/**
*************************************************************
* @file dni.c
* @brief Comprueba la letra del DNI (España)
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char letraDNI(int dni)
{
  char letra[] = "TRWAGMYFPDXBNJZSQVHLCKE";

  return letra[dni%23];
}

short verificaDNI(char *dni)
{
  if (strlen(dni)!=9)
      return 0;
  else
    return (letraDNI(atoi(dni))==dni[8]);
}

int main(int argc, char *argv[])
{
  char dni[20];
 
  printf ("Introduce tu DNI con letra (sin espacios): ");
  scanf("%s", dni);

  if (verificaDNI(dni))
    printf ("El DNI es correcto\n");
  else
    printf ("El DNI no es correcto\n");
   
  return EXIT_SUCCESS;
}

Descargar: dni.c (838bytes)

Redimensionar ventanas con Alt+Botón derecho en GNOME 2

Lunes, 2 de Abril de 2012 Gaspar Fernández Sin comentarios

Aunque GNOME2 ya está algo antiguo, aún lo uso en algún equipo viejo, y una cosa que yo utilizo muy a menudo es la redimensión de ventanas con Alt+Botón derecho del ratón (para mover también es muy rápido Alt+Botón izquierdo), aunque GNOME 2 trae por defecto la redimensión con Alt+Botón central lo que no me resulta muy cómodo a veces (sobre todo en portátiles cuando no hay botón central).

Para ello, aunque se puede hacer de forma gráfica, a veces es mejor un copia y pega de consola:

$ gconftool-2 –set /apps/metacity/general/resize_with_right_button –type=bool True

Algoritmos: Probar la existencia de un fichero con open() en C

Sábado, 31 de Marzo de 2012 Gaspar Fernández 2 comentarios

Estos días a varios de mis alumnos les ha surgido la necesidad de probar la existencia de un archivo haciendo la llamada al sistema open(). Es bastante sencillo, sólo hay que probar si éste se ha podido abrir (como lectura, por ejemplo, para no tocar el fichero en la medida de lo posible).
Una vez se abre el fichero pueden ocurrir varias cosas:

  • que la llamada a open() no devuelva error. Por tanto el fichero existe
  • que la llamada a open() de error:
    • si el resultado de errno es ENOENT (constante de error), ENOFILE en algunos sistemas o 2 (valor que generalmente vale dicha constante), el fichero no existe
    • si el resultado de errno es distinto, no podemos asegurar que no exista, pero tal vez el nombre de fichero no sea correcto, la ruta no exista, o no tengamos acceso a él.

Para contemplar estos casos creamos la función exists(). Incluyo la función y el código de ejemplo:

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
/**
*************************************************************
* @file exists.c
* @brief Verifica la existencia o no de un fichero
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

short exists(char *fname)
{
  int fd=open(fname, O_RDONLY);
  if (fd<0)         /* error */
    return (errno==ENOENT)?-1:-2;
  /* Si no hemos salido ya, cerramos */
  close(fd);
  return 0;
}

int main(int argc, char *argv[])
{
  int abre=exists("archivo");
  switch (abre)
    {
    case 0: printf ("El archivo existe\n"); break;
    case -1: printf ("El archivo no existe\n"); break;
    case -2: printf ("Ocurrió un error al abrir. %d (%s)\n", errno, strerror(errno)); break;

    default: printf ("Nunca veremos este mensaje");
    }

  return EXIT_SUCCESS;
}

Descargar: exists (1Kb)
Para ejecutarlo podemos probar:

$ ./exists
El archivo no existe
$ touch archivo
$ ./exists
El archivo existe
$ chmod -r archivo # Eliminamos permiso de lectura al fichero
$ ./exists
Ocurrió un error al abrir. 13 (Permission denied)

Puede que también seas de los que piensan que si hay algún error al abrir el archivo, da igual, lo marcamos como no existente, de todas formas no podemos hacer nada con él. Así, construimos una función exists() más intuitiva que devuelve 1 cuando existe y 0 cuando no existe o hay error:

1
2
3
4
5
6
7
8
9
short exists(char *fname)
{
  int fd=open(fname, O_RDONLY);
  if (fd<0)         /* error */
    return 0;
  /* Si no hemos salido ya, cerramos */
  close(fd);
  return 1;
}

Linux On Movies: Linux en TRON Legacy

Jueves, 29 de Marzo de 2012 Gaspar Fernández Sin comentarios

Hace más de un año que se estrenó la película, pero ahora es cuando decido incluirla aquí, que tengo capturas y tiempos concretos:
En el minuto 11 segundo 25 podemos ver el primer regalo… GNU/EMACS !!
tron_emacs
y para los que no sepan lo que es capaz de hacer Emacs… ahora lo vemos ejecutando un juego en Emacs lisp: hanoi-unix:
tron_emacs2
Aunque sólo sale un instante… igual que sólo sale durante un solo frame…
tron_ubuntu
¡ Ubuntu !
tron_unix
Luego, alrededor del minuto 21 y durante casi un minuto podemos disfrutar de un sistema Unix con su terminal, y ejecutando comandos existentes, como top, uname, history, whoami, y dentro de history, se ve kill, ps, cat, vi, en fin… algo que nos suena y no suele salir en una película estadounidense…

Encontráis más información sobre los guiños a GNU/Linux o Unix en la película en estos enlaces:
Tron Legacy, ¿qué sistema operativo usaba Flynn?
La veracidad UNIX de Tron Legacy

Algoritmos: Factorial y cálculo del seno por la serie de MacLaurin (Taylor alrededor de 0) en C

Martes, 27 de Marzo de 2012 Gaspar Fernández 2 comentarios

Algo más matemático vuelvo cuando propongo lo siguiente. Vamos a calcular un seno con la serie de MacLaurin. Ésta es la fórmula, extraída de Wikipedia.sin_maclaurin

Y este el algoritmo en 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
#define PI 3.141592653588

unsigned factorial(int n)
{
  if (n<1)
    return 1;
 
  return n*factorial(n-1);
}

double senoTaylor(double n)
{
  double t=0;
  int i;

  short signo=(n>=0);
  n=fabs(n);            /* Calculamos el valor absoluto */

  while (n>PI)      /* Reducimos el ángulo, de dos en dos cuadrantes*/
    {
      n-=PI;            /* 1 vuelta = 2PI radianes */
      signo=!signo;     /* cada PI, el signo cambia */
    }

  for (i=0; i<9; i++)
    t+=pow(-1, i)*pow(n, 2*(double)i+1)/(double)factorial(2*i+1);

  if (!signo)           /* Cambiamos el signo del ángulo */
    t*=-1;

  return t;
}

Con este algoritmo, cuantas más iteraciones demos, más preciso será el cálculo, aunque tenemos que tener cuidado de nos desbordar las variables, ya que el crecimiento de la función factorial es bastante rápido.

Es importante lo que hacemos al principio, el ángulo en radianes debe ser reducido, nos quedamos con un valor que esté siempre en la mitad superior del eje X (cuadrantes 1 y 2), este algoritmo es mejor con valores pequeños. Sería incluso mejor quedarnos con el ángulo en el primer cuadrante para que mejore la precisión.

Para probar el algoritmo, podemos utilizar este programa, en él utilizamos la función sin() de math.h y la comparamos con esta nueva función (aunque sin es más rápida y mejor, dejamos esta función de Taylor o MacLaurin con fines educativos):

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
/**
*************************************************************
* @file senoMaclaurin.c
* @brief Cálculo del seno con el algoritmo de MacLaurin
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*
* Compilar con:
*   $ gcc -o senoMaclaurin senoMaclaurin.c -lm
* para incluir la biblioteca matemática
*************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define PI 3.141592653588

unsigned factorial(int n)
{
  if (n<1)
    return 1;
 
  return n*factorial(n-1);
}

double senoTaylor(double n)
{
  double t=0;
  int i;

  short signo=(n>=0);
  n=fabs(n);            /* Calculamos el valor absoluto */

  while (n>PI)      /* Reducimos el ángulo, de dos en dos cuadrantes*/
    {
      n-=PI;            /* 1 vuelta = 2PI radianes */
      signo=!signo;     /* cada PI, el signo cambia */
    }

  for (i=0; i<9; i++)
    t+=pow(-1, i)*pow(n, 2*(double)i+1)/(double)factorial(2*i+1);

  if (!signo)           /* Cambiamos el signo del ángulo */
    t*=-1;

  return t;
}

int main(int argc, char *argv[])
{
  double n;
  printf ("Introduzca un numero: ");
  scanf ("%lf", &n);

  printf ("sen: %lf\n", sin(n));
  printf ("sen: %lf\n", senoTaylor(n));

  return EXIT_SUCCESS;
}

Importante, para compilar el proyecto, debemos utilizar gcc con el modificador -lm para poder utilizar math.h correctamente.

Descargar: senomaclaurin.c (1.2Kb)

Algoritmos: Año bisiesto y validación de fechas en C

Lunes, 26 de Marzo de 2012 Gaspar Fernández 2 comentarios

Para inaugurar la nueva sección de Algoritmos de este blog, empezaré presentando una función para calcular años bisiestos (lo podemos encontrar en Wikipedia en bastantes más lenguajes, pero, ¿por qué aquí no?

1
2
3
4
function bisiesto(int year)
{
  return (a%4==0) && ( (a%100!=0) || (a%400==0) );
}

Es decir, cada 4 años hay un año bisiesto, no cuando el año es múltiplo de 100, aunque sí cuando es múltiplo de 400.

Esta función, podemos utilizarla para validar fechas:

1
2
3
4
5
6
7
8
9
10
11
12
short valida_fecha(short d, short m, int a)
{
  short dias_mes[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

  if ( (d<=0) || (m<=0) )   /* Si el día, o mes es negativo o cero */
    return 0;           /* la fecha no es válida */

  if ( (m!=2) || (!bisiesto(a)) )
      return (d<=dias_mes[m-1]);
  else
    return (d<=dias_mes[1]+1);
}

Es decir, primero creamos un array con los días que tiene cada mes, para que sea de fácil acceso, si lo prefieres, puede ser un array estático: short dias_mes[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
Luego comprobamos si el mes es distinto de 2, o no el año no es bisiesto, en ese caso comparamos con el valor del array (es importante decir que el mes 1 es Enero, pero en nuestro array, Enero será el elemento 0 y por eso restamos 1. Si la condición no se cumple, verificamos que el día es menor o igual que los días de Febrero + 1 (29 días).

En el ejemplo a continuación vemos todo el 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
43
44
45
/**
*************************************************************
* @file valida_fechas.c
* @brief Valida una fecha introducida por teclado
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*************************************************************/


#include <stdlib.h>
#include <stdio.h>

short bisiesto (int a)
{
  return (a%4==0) && ( (a%100!=0) || (a%400==0) );
}

short valida_fecha(short d, short m, int a)
{
  short dias_mes[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

  if ( (d<=0) || (m<=0) )   /* Si el día, o mes es negativo o cero */
    return 0;           /* la fecha no es válida */

  if ( (m!=2) || (!bisiesto(a)) )
      return (d<=dias_mes[m-1]);
  else
    return (d<=dias_mes[1]+1);
}


int main(int argc, char *argv[])
{
  int a,m,d;

  printf ("Por favor, introduce una fecha (D/M/A)\n");
  scanf("%d/%d/%d", &d, &m, &a);
 
  if (valida_fecha(d, m, a))
    printf ("Fecha válida\n");
  else
    printf ("Fecha NO válida\n");

  return EXIT_SUCCESS;
}

Descargar archivo: valida_fechas.c (1Kb)

Visita otras webs de la red