Archivo

Archivo para la categoría ‘General’

Publicando en Facebook como página para mantener a nuestros fans

Lunes, 30 de Enero de 2012 Gaspar Fernández Sin comentarios

poesiabinaria

Si administras una página de Facebook, esto te interesa, ya que puedes gestionar las publicaciones de forma automática, por ejemplo, si tienes muchas cosas que publicar y quieres hacerlo de manera escalonada, para no hacerlo todo seguido y mantener alerta a tus seguidores.

Para poder poner en práctica todo esto, te recomiendo leer estos artículos anteriores:

Estudiando el tema

El problema de administrar páginas, es que tienes que estar identificado como estas, y además, hacerlo en modo offline. Por otra parte, las páginas tienen sus administradores y éstos son los usuarios que se pueden identificar en aplicaciones.
Facebook hace todo esto a través de palabras de acceso (access tokens), cuando un usuario se identifica en una aplicación se genera un token único para ese usuario y éste durará un tiempo, durante ese tiempo, la aplicación podrá realizar las acciones que el usuario le ha dado permiso para hacer.

En el caso de las páginas, debemos generar un access token que valga para la página en cuestión que queremos manejar. Ese access token debemos pasárselo a la llamada a la API para publicar.

Obteniendo los access tokens de las páginas que administro

Para ello hay que hacer la llamada a la API /me/accounts, con lo que obtendremos un listado completo de las páginas que administramos, ID de páginas (para poder realizar las publicaciones) y los access tokens necesarios para publicar en nombre de esas páginas.

Podemos modificar el programa que teníamos para identificarnos (index.php) de la siguiente manera (facebook_ext está para descarga en un post anterior):

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
<?php

$api_key = 'xxxxxxxxxxx';
$api_sec = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

require_once('facebook_ext.php');
// Definimos constantes
define(NEEDED_PERMISSIONS,  'publish_stream,offline_access,manage_pages');

// Definimos códigos de error
define(NOT_INSTALLED,      1);
define(NO_PERMISSIONS,     2);

$facebook = new FacebookExtended(array(  
                       'appId'  => $api_key,
                       'secret' => $api_sec,
                       'cookie' => true ,
                     ));

try
{
 
  $sesion = $facebook->getUser();
  if (!$sesion)
    throw new Exception('Aplicación no instalada', NOT_INSTALLED);

  echo "Estamos identificados en Facebook<br/>";
  echo "Usuario: ".$sesion."<br/>";

  $permissions = $facebook->askForPermissions(NEEDED_PERMISSIONS);
   
  if (!$permissions)
    throw new Exception('No tengo permisos suficientes', NO_PERMISSIONS);
 
  print_r($facebook->api('/me/accounts'));
}
catch (FacebookException $e)
{
  echo "Error de Facebook: ".$e->getCode().": ".$e->getMessage();
}
catch (Exception $e)
{
  switch ($e->getCode())
    {
    case NOT_INSTALLED:
      $facebook->loginUser();
      break;
    case NO_PERMISSIONS:
      $facebook->loginUser(NEEDED_PERMISSIONS);
      break;

    case MALFORMED_ARRAY:
      echo $e->getMessage();
      break;

    default:
      echo "Ocurrió un error no identificado";
    }
}
?>

Con este código, pediremos permiso para administrar páginas, para uso offline y para publicar mensajes en nombre del usuario. Cuando estemos identificados, nos devolverá un array con los nombres de las páginas, los ID y los access tokens. El array dentrá la siguiente forma:

Array
(
[data] => Array
(
[0] => Array
(
[name] => Nombre de página 1
[access_token] => xxxxxxx
[category] => Website
[id] => 123456789
)

[1] => Array
(
[name] => Nombre de página 2
[access_token] => xxxxxxx2
[category] => Website
[id] => 987654321
)
………………..
[n] => Array
(
[name] => Nombre de página N
[access_token] => xxxxxxxn
[category] => Website
[id] => 918273645
)
)
)

Se mostrarán todas las páginas que administramos. Ahora tendremos que anotar el ID de la página con la que queremos publicar y el access token para copiarlos en el código del programa encargado de publicar.

Publicando como si fuéramos una página de Facebook

Para hacer la publicación tenemos que hacer una llamada a /ID_de_página/feed especificando el access token con el que queremos publicar:

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
<?php

$api_key = 'xxxxxxxxxxx';
$api_sec = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

require_once('facebook_ext.php');

$facebook = new FacebookExtended(array(  
                       'appId'  => $api_key,
                       'secret' => $api_sec,
                       'cookie' => true ,
                     ));

try
{
  // ID de la página que queremos editar. La podemos ver en la URL de Facebook de la página
  $destino=189639191053576;
  $access_token='XXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
  $public='Probando mensajes automáticos con el modo offline';

  print_r( $facebook->api('/'.$destino.'/feed', 'post', array('access_token' => $access_token,
                                  'message' => $public)));  
} catch (Exception $e)
{
  echo "Ocurrió un error";
}
?>

Facil, ¿no? Si trabajamos un poco con todo esto podemos conseguir automatizar los mensajes que se van a publicar en Facebook desde las páginas que gestionamos.

Escribiendo en muros desde nuestra aplicación de Facebook

Domingo, 22 de Enero de 2012 Gaspar Fernández Sin comentarios

fb_website3 Uno de los usos más extendidos de las aplicaciones de Facebook es el de escribir en el propio muro o en el muro de nuestros amigos. Vamos a hacer un ejemplo de esto utilizando la Graph API de Facebook, a través de la biblioteca oficial que ellos nos dejan en GitHub. Para empezar a crear la aplicación os recomiendo leer este post anterior donde se dice paso a paso cómo se debe crear y cómo empezar a escribir código.

Pedir permiso para publicar

Antes de nada, debemos pedir permiso para publicar en el muro del usuario, para pedir permiso, lo podemos hacer en el mismo momento que ejecutamos el método getLoginUrl() pasando como parámetro un array cuyo único elemento será ’scope’ y su valor serán los permisos requeridos separados por comas. Por ejemplo:

1
$facebook->getLoginUrl(array('scope' => 'publish_stream'));

El permiso que debemos pedir para poder publicar en los muros de Facebook es stream_publish. Por otra parte, puede que ya hayamos identificado la aplicación con anterioridad, por lo que al estar dentro del sistema no hacemos llamada a getLoginUrl(), para ello debemos comprobar los permisos de que disponemos y eso lo hacemos con una llamada a la API a /usuario/permissions:

1
print_r($facebook->api('/me/permissions'));

Lo que nos devolverá un array con los permisos que dispone la aplicación, el array tendrá un elemento ['data'] que será otro array, cuyo elemento [0] contendrá otro array con los permisos:

Array ( [data] => Array ( [0] => Array ( [installed] => 1, [publish_stream] =>1 ) ) )

fb_website2

Publicar un mensaje en mi muro

Para publicar un mensaje en mi muro, debemos hacer una llamada a /usuario/feed y enviar por post la información referente al mensaje deseado:

1
$facebook->api('/me/feed', 'post', array('message' => $mensaje));

Código fuente demo

En este código fuente, he integrado excepciones para controlar los posibles errores (No identificado, No tengo permisos, Facebook me ha devuelto un array incorrecto), en este caso, los errores de identificación son salvables, pero los demás no. Para probar este código, debes cambiar la $api_key y la $api_sec:

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
<?php

$api_key = 'xxxxxxxxxxxxx';
$api_sec = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

require_once('facebook.php');

// Definimos códigos de error
define(NOT_INSTALLED,      1);
define(NO_PUBLISH_STREAM,  2);
define(MALFORMED_ARRAY,   90);

$facebook = new Facebook(array(  
                   'appId'  => $api_key,
                   'secret' => $api_sec,
                   'cookie' => true ,
                 ));

try
{
 
  $sesion = $facebook->getUser();
  if (!$sesion)
    throw new Exception('Aplicación no instalada', NOT_INSTALLED);

  echo "Estamos identificados en Facebook<br/>";
  echo "Usuario: ".$sesion."<br/>";

  // Obtenemos los permisos del usuario
  $permissions = $facebook->api('/'.$sesion.'/permissions');
  if (!isset($permissions['data'][0]))
    throw new Exception('Facebook ha devuelto un array mal formado', MALFORMED_ARRAY);

  if (!isset($permissions['data'][0]['publish_stream']))
    throw new Exception('No tengo permiso publish_stream', NO_PUBLISH_STREAM);

  $mensaje='Probando la publicación de mensajes en Facebook...';
  print_r( $facebook->api('/189639191053576/feed', 'post', array('message' => $mensaje)));

} catch (Exception $e)
{
  switch ($e->getCode())
    {
    case NOT_INSTALLED:
      $login_url = $facebook->getLoginUrl();
      header('Location: '.$login_url);
      die();
      break;
    case NO_PUBLISH_STREAM:
      $login_url = $facebook->getLoginUrl(array('scope'=>'publish_stream'));
      header('Location: '.$login_url);
      die();
      break;

    case MALFORMED_ARRAY:
      echo $e->getMessage();
      break;

    default:
      echo "Ocurrió un error no identificado";
    }
}
?>

Publicando en otros muros y páginas

Para publicar en el muro de otros usuarios, simplemente tenemos que sustituir la dirección de la llamada a la API donde publicamos de /me/feed a /usuario/feed donde usuario es el texto que aparece al acceder en la dirección de la página principal de Facebook de alguno de nuestros amigos (también puede ser su ID de usuario, pero ya es difícil de obtener).

Para publicar en páginas debemos sustituir el /usuario/feed por /id_de_pagina/feed donde el ID de la página es el número que aparece en la dirección de la página en cuestión, si por ejemplo, la URL de la página de Facebook de este blog es:https://www.facebook.com/pages/Poesía-Binaria/189639191053576 ; la id de la página es la que está en negrita.

Sólo teniendo el permiso publish_stream ya podemos publicar con el nombre de usuario que tenemos actualmente en cualquier lado dentro de Facebook.

Linux en Alias

Jueves, 19 de Enero de 2012 Gaspar Fernández Sin comentarios

alias_kdeEn la serie Alias, en el episodio 11 de la segunda temporada “Un Escalón Más Alto”, o “A Higher Echelon”, podemos ver este fotograma (casi en el minuto 17), en el que vemos un escritorio KDE y una terminal abierta.

Por otra parte, a lo largo del capítulo, se pueden ver varias pantallas como esta, en las que de fondo, si entornamos un poco los ojos, se está compilando con cc y con un argumento: “-D_GNU_SOURCE

alias_kde2

Fuente: http://groups.google.com/group/comp.windows.x.kde/msg/3100d0f3062fac2a

Creando una aplicación para Facebook (paso a paso)

Miércoles, 18 de Enero de 2012 Gaspar Fernández 2 comentarios

monitor_poesia

He decidido hacer esta guía porque Facebook anda cambiando los métodos para hacer aplicaciones y varias partes de su API. Los chicos de Facebook no paran de meter y sacar cosas y no dejan la página quieta. Después de hacer varias guías para “publicar definitivamente” en Facebook, van y cambian la forma de hacerlo.

Aunque parece que se han estabilizado, todos estos cambios han sido para llegar a la Graph API, se ha quedado bien hecha y estable, y yo creo que es posible aprender mucho de ella.

Aunque en esta guía empezaré desde el principio, desde que se crea la aplicación, paso a paso, ya que actualmente crear una aplicación vale para muchas cosas, no sólo para algo que resida dentro de Facebook sino para webs o clientes que funcionan fuera de Facebook e interactúan con la red social.

He de decir también que esta guía está hecha en Enero de 2012, por lo que si entras aquí en el futuro, puede que hayan hecho otro cambios definitivo más en Facebook.

Primer paso: Crear la App

Para ello debemos dirigirnos a https://developers.facebook.com/apps. Éste será nuestro centro de control de aplicaciones, donde podremos ver y editar los datos de éstas. Allí encontraremos en la parte superior de la página:

facebook_create_app

Pulsamos sobre Create New App, tras ello veremos un diálogo como este:

facebook_create_app_dialogEn el que en:

  • App Display Name debemos decir el nombre de nuestra aplicación, o si es una aplicación para identificarnos en una página, el nombre de la página. Es un nombre que la identifique.
  • App Namespace es el nombre que tiene nuestra aplicación en la URL de aplicaciones de Facebook. Es decir https://apps.facebook.com/AppNamespace . Dependiendo del ámbito de nuestra aplicación (si es accesible a través de Facebook.com o no) rellenaremos este campo o no.

Si continuamos, a veces la web de Facebook dará un error (comprobado el 7 de Enero de 2012), por lo que podemos volver a la página de las aplicaciones y continuar.

En la configuración de la aplicación, le damos a Edit Settings, y saldrá una pantalla así:

facebook_app_basica

Aquí podremos rellenar el Namespace (mencionado antes), el mail de contacto y el dominio donde está alojada la aplicación (muy importante, ya que si la aplicación no está en el dominio indicado, no funcionará; además de la categoría de nuestra aplicación.

Por otra parte, arriba vemos la App ID y la App Secret. La primera es el identificador de nuestra aplicación, y la segunda una clave secreta que no se debe compartir con nadie para que la aplicación pueda interactuar con Facebook, algo así como el nombre de usuario y contraseña de nuestra aplicación. (Yo lo he puesto aquí, pero en realidad lo he regenerado varias veces).

La aplicación que crearemos servirá para enlazar una página web con Facebook y poder utilizar esa información, para eso debemos rellenar el apartado App Domain (por ejemplo minutodecaos.com), con el dominio donde estén alojadas las páginas de la aplicación, si no, la aplicación no estará autorizada y, un poco más abajo Site URL con la dirección exacta donde estará alojada nuestra aplicación.

fb_website

Código fuente de la App

Es hora de crear nuestra aplicación en PHP. Lo primero que tenemos que hacer es descargarnos los archivos de la API desde esta dirección, dentro de GitHub. En principio trabajaremos en la web en modo local, por lo que creamos un directorio en nuestro disco duro (proyectos/facebook/tests/ dentro de mi home) Y descomprimimos los archivos dentro del directorio de nuestro proyecto. Aunque no es estrictamente necesario, yo lo he descomprimido dentro de lib/facebook, por lo que esos archivos estarán en $HOME/proyectos/facebook/tests/lib/facebook; y creamos el siguiente index.php (que luego subiremos al servidor junto con los ficheros de biblioteca de Facebook) :

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
<?php

$api_key = 'xxxxxxxxxx';
$api_sec = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

require_once('lib/facebook/facebook.php');

$facebook = new Facebook(array(  
                   'appId'  => $api_key,
                   'secret' => $api_sec,
                   'cookie' => true ,
                 ));

$sesion = $facebook->getUser();
if ($sesion)
  {
    echo "Estamos identificados en Facebook<br/>";
  }
 else
  {
    echo "No estamos identificados en Facebook ";
    $login_url = $facebook->getLoginUrl();
    echo '<a href="'.$login_url.'">Click para identificarte</a>';
  }

?>

Este código nos mostrará, la primera vez que entremos: “No estamos identificados en Facebook. Click aquí para identificarte”, eso no significa que no hayamos entrado a la página de Facebook, significa que la página web no te ha identificado como usuario y de ser autorizada, por eso hacemos click en la pantalla anterior y aparecerá lo siguiente:
fb_website1

Login automático

Si no queremos mostrar el enlace para identificarnos en la aplicación, podemos utilizar el siguiente código:

1
2
3
4
5
6
7
8
if ($sesion)
   echo "Estamos identificados en Facebook";
else
{
   $login_url = $facebook->getLoginUrl();
   header('Location: '.$login_url);
   die();
}

Mostrando datos de usuario, o cogiéndolos

En la parte de “Estamos identificados en Facebook”, podemos hacer:

1
2
3
4
5
6
7
if ($sesion)
{
   echo "Estamos identificados en Facebook<br/>";
   echo "Usuario: ".$sesion."<br/>";
   $userData = $facebook->api('/me');
   echo nl2br(print_r($userData, true));
}

Con estas líneas podemos ver información del usuario que está actualmente identificado en nuestra aplicación, y podremos utilizar los datos del array $userData:

  • ['name'] - Nombre completo del usuario
  • ['first_name'] - Nombre
  • ['last_name'] - Apellidos
  • ['link'] - Enlace a la página del usuario en Facebook
  • ['username'] - Nombre de usuario de Facebook
  • ['about'] - Frase personal
  • ['gender'] - Género
  • y mucho más que podemos ver cuando ejecutamos el ejemplo anterior

Ahora en Facebook, siempre que queramos acceder a información tanto de páginas, grupos, usuarios, etc, sólo tenemos que acceder a través de la llamada a api(’/localizacion’), con la palabra especial /me, como hemos visto para el usuario actual. Esto nos puede ayudar a identificarnos en nuestra web, sin pedir usuario, ni contraseña, a través de la Graph Api.

5 Aplicaciones que me salvan día a día

Domingo, 15 de Enero de 2012 Gaspar Fernández 3 comentarios

Son pequeñas aplicaciones sin las cuales no podría salir adelante día a día. Aunque a veces no las mime demasiado y no les haga mucho caso, deberían saber que son importantes para mí, para mi vida online y yo informático. He intentado establecer un orden, pero todas ellas son muy importantes, por lo que ocuparían todas la primera posición:

  1. Keepnote

    Es un software muy completo y ligero para tomar notas, clasificarlas y buscar dentro de ellas. De todos los que he probado es el que mejor sensación me ha dejado con respecto a su facilidad de uso, velocidad y características. Disponible en Linux, Mac y Windows

  2. KeepassX

    Es un gran programa para anotar nuestras contraseñas y anotaciones para cualquier tipo de identificación electrónica (webs, banca online, routers, etc). Puedes ordenar las contraseñas por categorías, asignarles iconos, buscarlas, etc. Es muy fácil de utilizar, y guarda la información cifrada para que nadie más que tú pueda verla. Disponible para Linux, Mac y Windows

  3. Grisbi

    Un software para organizar mi economía personal, saber cuánto dinero tengo en la cartera y en el banco, quién me debe y a quién le debo. Es muy completo y funciona en Linux, Mac y Windows.

  4. Emacs

    Llamadme friki, pero es mi editor de texto / IDE favorito para programar. Con multitud de opciones, automatizable, personalizable y todo a mano, aunque para aprender fue algo difícil en principio, una vez que sabes las cuatro cosas básicas, todo va sobre ruedas, y cada día me gusta más (excepto la opción deshacer, pero bueno, nadie es perfecto). Funciona en Linux, Mac, Windows, *BSD, Solaris y algunos más

  5. Hotot

    Un cliente para Twitter muy ligero, cómodo e intuitivo. Hay muchos clientes para microbloguear, pero con éste es con el que me siento más cómodo. Disponible para Linux, Mac, Windows y Chrome, aunque para Windows y Mac es bastante inestable aún.

En algunos de estos proyectos, y en otros más intento colaborar de diferentes formas, aunque a veces no en la medida que me gustaría, por falta de tiempo, como pasa siempre:

  • Clicks en la publicidad de sus webs oficiales
  • Perfeccionamiento de traducciones
  • Haciendo que más gente los utilice
  • Reportando bugs
  • Sugiriendo características

Y vosotros, ¿cómo colaboráis con vuestros proyectos preferidos? ¿Cuáles son esos 5 programas sin los que no podríais vivir?

Creando un menú selector de frecuencia de nuestra CPU

Lunes, 9 de Enero de 2012 Gaspar Fernández 2 comentarios

screenshot-05-01-2012-220103

A mí me gusta tener el control sobre la frecuencia de la CPU, ya sea porque muchas veces no estoy haciendo nada y lo quiero dejar al mínimo de velocidad para que se caliente menos, o porque algunas veces necesito un poco más de potencia, y otras veces lo dejo a su aire para que cambie de frecuencia solo.

El script se basa en llamadas a cpufreq-set de cpufrequtils, aunque la llamada tiene que ser con permisos de root, por lo que conviene ver antes este artículo, de todas formas, todo esto se hará paso a paso.

Requisitos

  • CPU soportada por cpufreq
  • Sudo / Gksudo
  • Cpufrequtils
  • Zenity
  • Xosd

Editando sudoers

Este script lo ejecutaremos desde nuestro usuario, pero la llamada a cpufreq-set necesita permisos especiales, por lo tanto, lo haremos con sudo. Si saltamos este paso, nos preguntará la contraseña para ejecutar los comandos, pero si no queremos que nos pregunte, debemos hacer esto.

Lo primero es ejecutar:

$ sudo visudo

Y en el archivo, debajo de la última línea sin comentario, escribimos:

mi_usuario   ALL= NOPASSWD : /usr/bin/cpufreq-set

Sustituyendo mi_usuario por el usuario que va a ejecutar el script. Si tenéis varios usuarios podéis añadirlos uno por línea, o ver el artículo mencionado antes para más opciones.

El script

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
#!/bin/bash

DIALOG=zenity
CPUFREQSET=`which cpufreq-set`
OSDCAT=`which osd_cat`

XOSD_FONT="-*-luxisans-bold-*-*-*-47-*-*-*-*-*-*-*"
XOSD_OPTIONS="--pos=bottom -o -210 -A center -c green -s 2 -d 2 -f "$XOSD_FONT;
tempfile=`tempfile 2>/dev/null` || tempfile=/tmp/test$$
trap "rm -f $tempfile" 0 1 2 5 15

if [ -z $CPUFREQSET ]
then
    echo "cpufrequtils no está instalado"
    exit
fi

if [ -z $OSDCAT ]
then
    echo "xosd no está instalado"
    exit
fi

if [ -e "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies" ]
then
    actual=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq`
    cnt=0
    for i in `cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies`
    do
    if [[ $i != $actual ]]; then
        cnt=`expr $cnt + 1`
    fi
    done
else
    echo "CPUfreq no está disponible"
    exit    
fi

simple_math()
{
    echo "scale=2; $1" | bc;
}

saca_freq()
{
    cnt=0
    for i in `cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies`
      do
      if [[ $cnt == $1 ]]; then
      frq=$i
      break
      fi
      cnt=`expr $cnt + 1`
    done
    echo $frq
}

pone_freqs()
{
    cnt=0
    for j in `cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies`
      do
      if [[ $j != $actual ]]; then
      frq=$frq":Cpufreq "$cnt":"`simple_math $j/1000000`" Ghz"
      fi
      cnt=`expr $cnt + 1`
    done
    echo $frq
}

pone_governors()
{
    possible_governors=([0]=powersave [1]=conservative [2]=ondemand [3]=performance)
    governor_command=([0]=Powersave [1]=Conservative [2]=Ondemand [3]=Performance)
    governor_text=([0]="Ahorro de energía" [1]="Modo conservador" [2]="Modo Bajo demanda" [3]="Modo rendimiento")
    available_governors=`cpufreq-info -g`

    for (( g=0; g<${#possible_governors[*]}; g++ ))
    do
    # echo $g
    # echo ${possible_governors[$g]}
    gov=${possible_governors[$g]}
    if [ -n "`echo $available_governors | grep $gov`" ]
    then
        frq=$frq":"${governor_command[$g]}":"${governor_text[$g]}
    fi
    done
   
    echo $frq
}

freqs="Exit:No hacer nada"
freqs=$freqs`pone_freqs`
freqs=$freqs`pone_governors`

OLD_IFS=$IFS
IFS=":"


$DIALOG --width=240 --height=250 --title="Selector de frecuencia" --list --column "ID" --column "Frecuencia" --hide-column 1 --text "Elige una opcion" $freqs > $tempfile

retval=$?
IFS="${OLD_IFS}"

choice=`cat $tempfile`

comando=`echo $choice | cut --delimiter " " --fields 1`
modif=`echo $choice | cut --delimiter " " --fields 2`

case $retval in
  0)
    case $comando in
        "Exit")
        echo "Salir" | osd_cat $XOSD_OPTIONS
        ;;
        "Cpufreq")
        freqdef=`saca_freq $modif`
        gksudo "$CPUFREQSET -g userspace"
        gksudo "$CPUFREQSET -f $freqdef"
        echo "Velocidad actual "`simple_math $freqdef/1000000`"Ghz" | $OSDCAT $XOSD_OPTIONS
        ;;
        "Powersave")
        gksudo "/usr/bin/cpufreq-set -g powersave"
        echo "Modo ahorro de energía" | $OSDCAT $XOSD_OPTIONS
        ;;
        "Conservative")
        gksudo "/usr/bin/cpufreq-set -g conservative"
        echo "Modo conservador" | $OSDCAT $XOSD_OPTIONS
        ;;
        "Ondemand")
        gksudo "/usr/bin/cpufreq-set -g ondemand"
        echo "Modo bajo demanda" | $OSDCAT $XOSD_OPTIONS
        ;;
        "Performance")
        gksudo "/usr/bin/cpufreq-set -g performance"
        echo "Modo rendimiento" | $OSDCAT $XOSD_OPTIONS
        ;;     
    esac;;
  1)
    echo "Cancelado";;
  255)
    echo "Escape";;
esac

Notas del script

  • He optado por filtrar el resultado de cpufreq-info -g y cambiar las palabras, me gusta filtrar todos los datos que me llegan.
  • Añadí la primera opción para no hacer nada, porque me resultó más cómoda cuando quiero cancelar el menú
  • zenity nos devuelve dos cosas: El resultado de la ejecución y el valor elegido, por tanto el primer valor lo podemos averiguar del script, el segundo lo devuelve como texto, por eso lo almacenamos en un archivo temporal
  • Para cambiar el tipo de letra, tenemos que ejecutar xfontsel, desde ese programa, elegir el tipo de letra y copiar el texto que nos devuelve a la variable XOSD_FONT
    • Por último…

      Para disfrutar de este script, lo bueno es asociarlo a una tecla, por ejemplo yo lo tengo en Meta+F3 y con ello se puede cambiar la frecuencia rápidamente.

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

C.I. XIII: Tutorial Emacs, Taza VIM, supercomputador 10 petaflops, OSWC, Ubuntu ya no es el rey, IE baja y Chrome sube

Domingo, 27 de Noviembre de 2011 Gaspar Fernández Sin comentarios

Hace mucho que no recopilo enlaces, y es que a veces, por trabajo, ni puedo leer las noticias, ni el twitter a gusto, y mucho menos profundizar en algunos temas.

  • Curso intensivo de EMACS por parte de IBM. Me entero desde el blog MuyLinux y ya que es mi entorno de desarrollo preferido, tengo que promocionarlo un poco.
  • Aunque la noticia tiene un mes (K-Computer superó la barrera de los 10 petaflops). Vamos, ¡ una verdadera máquina para hacer raíces cuadradas ! Recuerdo cuando hacía simulaciones en los ordenadores de la facultad y nos tirábamos 2 ó 3 minutos a que salieran los resultados. Con uno como estos para todo el aula, hubiéramos terminado las prácticas en 20 minutos. Bueno, el paralelismo cuenta, hubiéramos aprendido algo muy interesante.
  • Conferencia Internacional de Open Source: Han cambiado de sede, y de fechas, ahora anuncian la Open Source World Conference para Granada, los días 12 y 13 de Enero. Aunque ya está en curso de forma anual la Libre Software World Conference, todos estos eventos son necesarios para el avance del Software Libre. ¿La cancelarán los políticos dos semanas antes de abrir sus puertas? ¿Invertirán menos dinero en las jornadas? ¿ Se meterá algún patrocinador que nada tenga que ver con el software libre?¿Predicarán con el ejemplo?  Se supone que deberían usar este tipo de software para que sea creíble que funciona.
  • Ubuntu, ya sí que no es la distribución más popular. Hace tiempo Ubuntu era la que más gustaba a la gente, aunque personalmente nunca me atrajo bastante, hace un par de años alcanzó un grado de estabilidad importante, y no estaba mal. Pero no quisieron (ni deben) quedarse estancados, pero parece que muchas decisiones que han tomado les han pasado factura.
  • Taza Vim. Si antes ponía un tutorial para Emacs, ahora es una cheatsheet en forma de taza de desayuno para aquellos que utilizáis Vim !

Bueno, también quiero compartir el gráfico de las últimas estadísticas de StatCounter, donde vemos la pérdida de cuota de mercado de Internet Explorer, que va por el 40%, y aunque sigue siendo el más usado (que tenga cuidado en Europa), muchos usuarios han preferido prescindir de una aplicación que viene instalada en su ordenador y han instalado otra mejor. También podemos ver que Chrome ha crecido mucho estos meses, y ya le planta cara seriamente a Firefox, llegando a superarle en algunos lugares:

112011stc 112011stc_eu 201111stdes
En todo el mundo En Europa En España

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);
}

Tiempos difíciles

Martes, 22 de Noviembre de 2011 Gaspar Fernández 2 comentarios

Llevo muchos días saturado de trabajo y no tengo mucho tiempo para escribir en el blog. Suelo dedicar un tiempo para contestar los correos y los comentarios que dejáis, pero, aunque tengo muchos posts en la cabeza no tengo tiempo para escribirlos.

Aunque intento sacar unos minutos a la semana :)

Mis disculpas.

Categories: General Tags:

Visita otras webs de la red