Weblogs Código

Poesía Binaria

Controlando un display de 7 segmentos con Arduino y un botón por interrupción.

julio 28, 2017 07:50

Arduino 7 segmentosRetomo mis posts sobre Arduino (después de muchísimo tiempo) con un pequeño ejemplo que monté hace unos años, pero el post nunca llegó a salir. Se trata de la utilización de un display de 7 segmentos desde la propia placa Arduino. Estos displays, al final son 7 barras led con las que podemos representar números y letras dependiendo de las que encendemos y las que no. En mi ejemplo, haremos un contador de 0 a 9 controlado por un pulsador.

El display

Para ver más a fondo cómo funciona el display, lo más fácil es ver este dibujo:

Este ejemplo muestra un display de cátodo común, ya que las patillas centrales van a gnd (sólo hay que conectar una) y las demás (a, b, c, d, e, f, g, dp) podrán ir conectadas a VCC. Todo depende de las que queramos encender. En mi caso, alimento cada uno de los leds con unos 3.6V.

También existen displays de ánodo común, como el que utilizo en este proyecto. En este caso conecto el display a VCC (5V) y cada uno de los segmentos van conectados a GND a través de una resistencia.

Conectar el display directamente a Arduino

Es una opción. Nuestro display tiene 7 segmentos, y normalmente los Arduinos (Duemilanove, Uno, Mega…) suelen tener muchos pines de entrada-salida (más de 7) configurables y capaces de soportarlo. Así que perfectamente podemos conectar cada segmento a un pin del Arduino y hacer un programa que escriba valores en dichos pines.

En principio no es mala idea, nosotros controlaremos al 100% la iluminación de los segmentos. Aunque podemos tener varios problemas:

  • Nuestro Arduino se calentará. El display al final pide potencia, y puede que nuestro Arduino trabaje en exceso para entregar la potencia necesaria para iluminar el display. Podemos poner resistencias más grandes, pero el display lucirá menos.
  • Son muchos pines, para controlarlos podemos usar digitalWrite() y es un poco tostón controlar los digitalWrite() de cada número que vamos a representar. Por otro lado podemos hacerlo usando los PORT del Atmega (lo veremos en el ejemplo), pero claro, el PORTD (pines 0-7) no podemos acapararlo entero porque por ejemplo las interrupciones de algunos Arduino (como el mío, un Duemilanove, aunque sea algo viejo) están en los pines 2 y 3 por lo que no podremos utilizarlos. Y el PORTB (pines 8-13) sólo son 6 por lo que no son suficientes. Así que tendremos que pasar parte por un puerto y parte por otro puerto. Pero a fin de cuentas, si queremos hacer algo más con nuestra placa hemos gastado muchos pines para este propósito.
  • Impensable ya pasar el montaje a un circuito a base de ATtiny, por ejemplo.

Por poner un ejemplo más, si queremos utilizar puertos en lugar de digitalWrite(), lo primero es mirar el datasheet del chip que estemos utilizando, en mi caso Atmega168. Tendremos que mirar a qué pines corresponden los puertos. Yo utilizaré el puerto D y el puerto B para conectar el display.

Lo primero será crear un array con los valores de los segmentos que corresponderán a cada dígito. Daremos valores en hexadecimal porque serán más fáciles de manejar cuando tienes algo de vicio, sobre todo cuando debemos mirar el valor en binario de cada uno de los valores. En mi array, como mi display es de ánodo común tengo que mandar ceros a los dígitos que quiero encender. En este caso, sólo será un ejemplo, haremos algo así:

1
int disp[10]={0x81, 0xdd, 0x43, 0x45, 0x1d, 0x25, /**/0x21, 0xcd, 0x01, 0x05};

Con este array, hacemos que la posición 0 contenga 1000 0001 (al 0 sólo le falta un segmento, el último bit no lo usaremos), en la posición 1 tendremos 1101 1101 (el 1 sólo tiene dos segmentos activos), en la posición 2 tendremos 0100 0011 (el dos sólo tiene 2 segmentos desactivados), etc.

Y, para enviar los valores a los puertos podríamos utilizar el desplazamiento binario (shift):

1
2
PORTB = disp[5] >> 3; // A la parte baja del PORTB
PORTD = disp[5] << 4; // A la parte alta del PORTD

Los puertos

Veamos primero un diagrama de los pines del Atmega368, por ejemplo (muy parecido al del Atmega168:

Vemos que hay pines etiquetados como PB0..PB7, PC0..PC6, PD0..PD7. Corresponderán a los puertos B, C y D respectivamente. Si hemos manejado Arduino anteriormente habremos visto cómo digitalRead/digitalWrite nos puede solucionar la vida cuando se trata de leer o escribir el valor de un pin concreto. Aunque lo que yo quiero es definir el valor completo de un puerto. Es decir, enviar, por ejemplo un valor 11011011 de una sola vez a todos los pines del puerto D, por ejemplo.

Para ello, lo primero que tenemos que hacer será definir una máscara de entrada/salida de los pines del puerto, lo que anteriormente hacíamos con pinMode([pin], OUTPUT); pero esta vez lo haremos definiendo en la función setup() o en cualquier otro sitio la variable DDRD dando 1 al bit correspondiente con el pin que queremos que sea de salida (OUTPUT) o 0 si queremos que sea de entrada (INPUT):

1
2
3
DDRD=0xff; // Puerto D entero como salida
DDRD=0xdb; // 11011011
DDRD= 0 | 1 << PD5 | 1 << PD3 | 1 << PD1; // Sólo pins 1, 3 y 5 como salida.

Con la última forma de hacerlo, podemos pensar que se realizan operaciones en el chip e irá más lento, pero al ser PD5, PD3 y PD1 constantes, en tiempo de compilación se procesará el número y el programa real se ejecutará con una sola instrucción, por lo que es tan rápido como el caso que hay justo encima en el que nosotros hemos calculado el número.

Ahora para escribir valores en el puerto puerto, escribiremos la variable PORTD y para leer valores, leeremos la variable PIND.

Decodificador de 7segmentos 7447, 7448

Para arreglar los problemas mencionados anteriormente (potencia y número de pins), vamos a utilizar el circuito integrado 7447, ya que nuestro display es de ánodo común. Si fuera de cátodo común utilizaríamos el 7448. Este es un circuito TTL decodificador de BCD (binary-coded decimal, o decimal codificado en binario) a display de 7 segmentos. Ya que el sistema utilizado es digital no se garantiza que la salida para valores mayores a 9 sea la misma en todos los chips. Dada su tecnología requiere una corriente baja de entrada, de algunos microamperios, y proporciona una corriente de salida del orden de los miliamperios, por lo que puede hacer las veces de amplificador de la señal haciendo que nuestro Atmega no sufra.Haremos las conexiones como en el siguiente diagrama (como en la foto de portada):

Éstos son los materiales utilizados:

  • Arduino Duemilanove
  • Display de 7 segmentos de ánodo común
  • DM74LS74N (decodificador BCD-7 segmentos)
  • 1 diodo led (es para jugar, sólo parpadeará indefinidamente
  • 8 resistencias de 1K
  • 1 resistencia de 10K (para el pulsador)
  • 1 switch

El esquema del proyecto será el siguiente:

Vamos al 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
int num=0;

ISR(INT0_vect)
{
  num++;
  if (num>9)
    num=0;
   
  PORTB=0xf0 & PORTB | 0x0f & num;
}

void setup()
{
  // put your setup code here, to run once:
  DDRB=0xff;
  DDRD= 0;
  pinMode(LED_BUILTIN, OUTPUT);
  PORTB=0xf0 & PORTB | 0x0f & num;

  EICRA=0x03;
  EIMSK=1;
 
  sei();
}

void loop()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);                      
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
}

Empecemos por setup(). En donde con DDRB preparamos el puerto B como salida y el puerto D como entrada. Como es el puerto entero, le damos un 0 y listo. Preparamos la salida del led, aunque no haría falta especificar el pinMode porque el LED_BUILTIN apunta al pin 13 que es del puerto B que ya está marcado como salida, pero dejo esa línea ahí por si cambiamos el led de sitio. Simplemente es un led que parpadea para demostrar el funcionamiento de la interrupción.

Seguidamente presentamos el número en el display, aunque ya que el display y el led comparten puerto (el puerto B para todos), quise poner la parte alta del puerto B tal cual está (0xf0 & PORTB) y en la parte baja del puerto B pongo el número a representar (0x0f & num).

EICRA y EIMSK son dos valores que tengo que establecer para que mi Atmega escuche a las interrupciones hardware. Esto depende mucho del chip y no en todos los Arduinos funcionará igual y, aunque la plataforma Arduino tiene su propia manera de hacer las interrupciones, he querido poner esta forma aquí.

Por último llamo a sei() para activar el flag de interrupciones de Atmega.

Luego en el loop() sólo me encargaré de actualizar el estado del led, apagarlo y encenderlo sin preocuparme por el pulsador. Por último, tenemos ISR(INT0_vect), una función donde estableceremos las acciones que se llevan a cabo cuando se activa la interrupción. En este caso incrementar el número y enviarlo a PORTB.

The post Controlando un display de 7 segmentos con Arduino y un botón por interrupción. appeared first on Poesía Binaria.

» Leer más, comentarios, etc...

Adrianistán

Todo lo que debes sobre las tarjetas de crédito, débito y prepago

julio 27, 2017 03:44

Este artículo es bastante extenso. Si te interesan los detalles más técnicos puedes ir al final.

Nacieron en la primera mitad del siglo XX. Originalmente, estas primeras tarjetas no eran más que una línea de crédito de un determinado establecimiento. En 1924 por ejemplo, General Petroleum Corporation emite una tarjeta para poder adquirir gasolina. En realidad, lo que sucedía al usar esa tarjeta era que contraíamos una deuda con la compañía, que posteriormente había que pagar en efectivo. De este modo, funcionaban de forma similar a como cuando dejamos que nos apunten algo en la cuenta del bar.

Esto cambió en 1949, cuando Franck McNamara estaba cenando con unos amigos y discutiendo precisamente sobre este tipo de tarjetas. Finalmente al llegar a la hora de pagar, McNamara pudo comprobar que se había dejado la cartera en casa. Tuvo que llamar a su mujer para que le llevase el dinero. Pasó tal vergüenza que se propuso acabar para siempre con este tipo de situaciones. Así es como nació Diners Club, la primera compañía de tarjetas de crédito del mundo.

Frank X. McNamara
Una de las primeras tarjetas Diners Club

Otros dicen que no llamó a su mujer sino que salió del aprieto dando su tarjeta de visita y anotando la cantidad debida. Sea cual sea la historia verdadera, lo cierto es que el sistema de funcionamiento de una tarjeta de crédito es ese. Nosotros cuando pagamos con una tarjeta de este tipo estamos emitiendo deuda, que a final de mes o cuando más nos convenga, pagamos íntegramente o a plazos. Diners cobraba comisiones de mantenimiento a los poseedores de las tarjetas y comisiones por transacción a los establecimientos que las admitían. Esta es la principal diferencia respecto a las tarjetas de débito y las tarjetas prepago.

Logotipos en un establecimiento de Master Charge (actual MasterCard) y BankAmericard (actual VISA)

A partir de ese momento empiezan a surgir más compañías, Bank AmeriCard o Master Charge (VISA y MasterCard actualmente). En España se aceptaron tarjetas de crédito por primera vez en el año 1954 y por aquel entonces eran simples tarjetas de cartón con un número y su titular, que los hoteles y restaurantes de la época anotaban para posteriormente reclamar el pago a Diners. La primera tarjeta emitida por un banco español no llegaría hasta 1978, por el Banco de Bilbao y se trataba de una AmeriCard. Las tarjetas de crédito son el tipo de tarjetas más admitidas en el mundo y suelen llevar asociadas ventajas tales como seguros, programas de puntos, … Al pasarnos una factura a final de mes es más fácil saber cuanto hemos gastado en un mes. Sin embargo estas tarjetas tienen un gran peligro. Nos dejan a nuestra disposición una gran cantidad de dinero, y si no somos capaces de pagar las cuotas a tiempo podemos enfrentarnos a unos intereses de devolución en la mayoría de los casos de dos cifras.

Las primeras tarjetas de crédito emitidas en España fueron las BankAmericard del Banco de Bilbao

Como vemos, las tarjetas de crédito no necesitaban ninguna tecnología en especial para funcionar, las de débito y las prepago, sí que lo necesitan. No obstante, todas las tarjetas de crédito hoy en día usan tecnología, de hecho existen tarjetas que operan en varios modos, crédito y débito. ¿Cómo funcionan las tarjetas de débito?

Las tarjetas de débito son lo más parecido a pagar en efectivo. Cuando pagamos, estamos pagando con nuestro dinero. En algunas se nos permite caer en números rojos (VISA Debit) mientras que en otros se comprueba la existencia de fondos (VISA Electron). Inmediatamente a la compra, el dinero se descuenta de la cuenta bancaria asociada. Estas tarjetas suelen tener gastos de mantenimiento menores (en España los bancos las suelen dar gratis) y son prácticamente igual de admitidas que las de créditos en cajeros automáticos, establecimientos y compras por Internet. No obstante, las tarjeta de débito no son aceptadas en todos los sitios, por dos motivos:

  • Las tarjetas de débito (como VISA Electron) requieren conexión a la cuenta bancaria de tu banco. Este proceso es más complicado que simplemente anotar que debes 50€ a fulanito. Otras tarjetas como VISA Debit pueden funcionar en puntos de pago offline ya que pueden dejarte en números rojos (por tanto, el comerciante se asegura que siempre va a recibir el dinero, aún sin saber cuanto dinero tienes).
  • Las tarjetas de débito no dejan gastar más dinero del que hay en cuenta (o pueden dejar, pero no demasiado).

Por estos dos motivos, puede ser que una tarjeta de débito no llegue a funcionar en algún país extranjero (aunque es cada vez más raro) o simplemente no se admitan para poder así cobrarte gastos imprevistos (alquileres de coches, hoteles, …). Si bien en esto las tarjetas de débito han ido mejorando y en muchas existe la posibilidad de reservar una cantidad de dinero de la tarjeta. Eso quiere decir que al hacerel primer pago o la entrada, se nos cobra una parte y se nos reserva otra. Esa cantidad reservada a efectos prácticos no podemos usarla para nada pero técnicamente sigue siendo nuestro. Cuando se acaba el plazo y no ha hecho falta recurrir a ese dinero de reserva, el comerciante libera ese dinero y ya lo podemos usar en nuestra tarjeta.

Las tarjetas prepago funcionan con tecnología similar a las de débito, salvo por el detalle de que no tienen asociada una cuenta bancaria como tal. En vez de eso, las tarjetas prepago tienen una cuenta propia, que debemos recargar antes de efectuar pagos o retirar dinero. Las tarjetas prepago se han vuelto muy populares para menores de edad y compras por Internet, donde solo se recarga la tarjeta con la cantidad máxima que consideremos necesario.

La tarjeta Antes de BBVA es una tarjeta prepago

Por último, existe otro tipo de tarjeta, aunque apenas usadas en compras, se trata de las tarjetas monedero. Su diferencia principal respecto a las tarjetas prepago reside en como se realiza el pago. En una tarjeta prepago, se realiza una conexión al emisor de la tarjeta, como con las de débito, mientras que en una monedero no. En una tarjeta monedero el dinero se almacena en la propia tarjeta, de forma cifrada. Este sistema no es tan popular y en España que yo sepa ningún banco emite tarjetas de este tipo. Son usadas sin embargo en algunos sistemas de transporte público o de empresas, donde se puede descontar el dinero instantáneamente, aunque no haya conexión con un servidor central. La forma de recargar estas tarjetas es acudir con la propia tarjeta a un punto de recarga, no pudiendo realizarse por Internet o sin la tarjeta.

Tarjeta monedero emitida por Caja Duero para el transporte urbano de Valladolid

Mencionar también las tarjetas ATM. Estas tarjetas únicamente pueden usarse en los cajeros para sacar dinero, no pudiéndose usar en las compras. De este modo funcionan similar a algunas libretas de ahorro. Sin embargo, he de reconocer que personalmente no he visto ninguna tarjeta ATM.

Redes bancarias

Si nos centramos en tarjetas de crédito, débito y prepago tenemos que tener en cuenta las redes interbancarias. Estas permiten las conexiones entre las redes de los distintos bancos y permiten por ejemplo que podamos sacar dinero en un cajero de EspañaDuero cuando nuestra tarjeta la ha emitido N26 Bank o que podamos pagar en el extranjero. En el sistema bancario, no existe nada parecido a Internet (la red de redes) y cada red tiene sus propios puntos de acceso. Así pues, no todos los cajeros tienen por qué poder conectarse a todas las redes existentes, con los consiguientes problemas (no poder sacar dinero, no poder pagar,…). En tarjetas de débito esto es más problemático. En España existen tres redes locales aunque funcionan también redes internacionales.

  • Servired, la mayor red intranacional de España, con el BBVA, Bankinter, Deutsche Bank, Sabadell, Bankia, CaixaBank, Cajamar, las cajas rurales que están unidas por el Banco Cooperativo Español, Triodos Bank, Self Bank, Banco Mediolanum, Caixa Geral, Laboral Kutxa, Abanca y otros bancos más pequeños.
  • 4B, originalmente para Banco Santander, Banco Popular, Banco Pastor y Banesto, ahora también ING, Targobank, Openbank, Banca March, Cetelem, Inversis, Andbank y alguno más.
  • Euro6000, usada antiguamente por las cajas de ahorros, hoy en día de sostiene gracias a Unicaja, EspañaDuero, Ibercaja, Kutxabank, Liberbank y EVO Banco.
Cajero perteneciente a la red intranacional Servired. También aparecen logos de PLUS, Cirrus, Tarjeta 6000, Eurocheque, Eurocard,…
Indicativo del Banco Pastor y Telebanco 4B
IberCaja, con sede en Zaragoza, es una de las entidades que sigue dentro de Euro 6000

Estas redes se comunican con otras redes similares de otros países o bien pueden recurrir a una red internacional, como Cirrus. Aunque después de la crisis de 2008 muchos bancos y cajas se han fusionado entre sí, parece que las tres redes siguen operando de forma independiente.

Logo actual de la red interbancaria Cirrus

Tarjetas

Vamos ahora a ver las principales empresas que se dedican a las tarjetas:

VISA (anteriormente Bank AmeriCard) es quizá la tarjeta más conocida del mundo. Es la tarjeta líder en España. A nivel global es la segunda red más importante (solo superada por UnionPay). Las tarjetas VISA operan a través de la red interbancaria PLUS. VISA dispone de varios tipos de tarjeta: VISA, VISA Debit, VISA Electron y V Pay. VISA es de crédito, VISA Debit es la versión más libre de las de débito. Permite dejarte en números rojos ya que al hacer un pago no se comprueba la disponibilidad de fondos. Es por ello que la VISA Debit puede usarse en algunos sitios donde solo se admiten tarjetas de crédito por ese mismo motivo, el comerciante se puede asegurar que va a recibir su dinero aunque no sepa si tu cuenta tiene fondos. VISA Electron por contra, verifica que haya fondos suficientes en la cuenta, así que necesita que exista una conexión con el banco. Desde hace no mucho existe V Pay, una tarjeta de débito mucho más moderna, emitidas solo con chip y que solo funcionan en SEPA (Single Euro Payments Area). Han sido diseñadas para ser usadas en transacciones pequeñas y por lo general funcionan de forma similar a VISA Electron.

Logo antiguo de VISA Electron
PLUS es la red interbancaria de VISA
V Pay es la gama más moderna de VISA, sólo válida en territorios SEPA

MasterCard, la gran competidora de VISA en España. MasterCard entro en el mercado europeo con las adquisiciones de Eurocard y Europay. Su red interbancaria es Cirrus. Posee varias tarjetas: MasterCard, Debit MasterCard y Maestro. MasterCard es la línea de tarjetas de crédito, Debit MasterCard es una línea de tarjetas de débito compatibles con puntos offline y números rojos (como VISA Debit) y Maestro es comparable a VISA Electron, ya que requiere comprobación de fondos. No obstante, Maestro también puede funcionar como tarjeta prepago y las tarjetas Maestro son distintas al resto de tarjetas MasterCard y VISA. En ciertos países, Maestro funciona como Debit MasterCard, por lo que no requiere autorización electrónica. Las tarjetas Maestro también son compartidas en algunos países con algún proveedor local de tarjetas, como Girocard en Alemania. Maestro fue de las primeras tarjetas donde no había que firmar, sino que se introducía un PIN al comprar, tanto si se compra usando chip como con banda magnética. Muchas Maestro que no pueden usarse en el extranjero e Internet. En España, las tarjetas Maestro nunca han llegado a ser populares.

Logos antiguos de MasterCard, Cirrus y Maestro

American Express es la tercera entidad de tarjetas en España. Sin embargo, debido a sus altas comisiones, es fácil encontrar tiendas que no la admitan.

Discover y Diners Club son bastante usadas en Estados Unidos y otros países como Croacia. En España son bastante difíciles de encontrar y se suelen aceptar únicamente en establecimientos turísticos. Hasta hace años eran empresas con tarjetas independientes, no obstante, Discover adquirió Diners Club hace años. Discover se centrará en el mercado doméstico estadounidense y Diners Club, que era más usada internacionalmente, seguirá con la expansión internacional.

JCB, se trata de una entidad japonesa de tarjetas. Es usada sobretodo por japoneses y coreanos.

UnionPay, es la compañía de tarjetas más grande del mundo, aunque la mayor parte de sus transacciones se concentran en China. Solo las emiten bancos chinos.

UnionPay es la tarjeta más usada del mundo desde 2015

RuPay, son unas tarjetas de amplio uso creadas con intervención del gobierno de la India con el deseo de que todos los habitantes de la India pudieran disponer de esta forma de pago.

Otras tarjetas pueden ser Bancomat (Italia), Elo (Brasil) o Dankort (Dinamarca). Este tipo de tarjetas, debido a su poca popularidad suelen ser compatibles con VISA y MasterCard en el extranjero, mientras que en sus países de origen usan su red interna.

Elo es muy usada dentro de Brasil

¿Cómo funcionan las tarjetas?

Ahora vamos a ver como se puede pagar con las tarjetas de crédito, débito y prepago. En primer lugar comentemos algo sobre el número de las tarjetas. Estas tienen normalmente 16 dígitos, aunque pueden llegar a 19 dígitos. Se numeran por lotes, siendo los primeros dígitos los correspondientes a la empresa. Es posible reconocer la empresa detrás de la tarjeta según su número o PAN.

Los PAN siguen una estructura, que aunque presenta longitud variable, está bien definida.

El primer dígito corresponde al Major Industry Identifier (MII). Indica a que sector pertenece la empresa de la que es la tarjeta. Los sectores son:

Dígito Sector
0 Uso interno
1 Aerolíneas
2 Aerolíneas y otros
3 Viajes, entretenimiento, finanzas
4 Finanzas
5 Finanzas
6 Finanzas
7 Petroleras
8 Salud y telecomunicaciones
9 Sin asignar

Aunque actualmente, la mayoría de tarjetas son de finanzas. El MII y los siguientes 5 dígitos forman el BIN o Bank Identification Number y sirven para idenficar al banco emisor de la tarjeta y enrutarlo. Los siguientes dígitos excepto el último forman parte del identificador de tarjeta personal, asignado por el banco a sus clientes. El último dígito es de comprobación y se calcula con el algoritmo de Luhn.

Número(s) de inicio Longitud número de tarjeta Empresa
22 a 27 16 MasterCard
30 14 Diners Club
34 15 American Express
35 16 JCB
36 14 Diners Club
37 15 American Express
38, 39 14 Diners Club
4 16 VISA
5018, 502, 503, 506 12 a 16 Maestro
5019 16 Dankort
51 a 55 16 MasterCard
56 a 58 12 a 19 Maestro
60 16 Discover
62 16 a 19 UnionPay
622, 64, 65 16 Discover
639, 6220 12 a 19 Maestro
88 16 a 19 UnionPay

Esta tabla no es exhaustiva pero puede servir de referencia rápida. Por ejemplo, aunque VISA empieza siempre por 4, no todas las que empiezan por 4 son VISA siempre, ya que existen tarjetas Elo que también empiezan por 4. No obstante, se trata de rangos tan pequeños que la lista sería muy larga si fuese exhaustiva.

Banda magnética y firma

Estas tarjetas, mejorando las de cartón, agilizan el proceso de pago pero ni introducen ninguna medida de seguridad extra. Se definen según el estándar ISO/IEC 7813 e ISO/IEC 7811. Incluyen una banda magnética desde la que se puede leer el número de la tarjeta, la fecha de caducidad, los nombres del titular y algunos datos técnicos. Las tarjetas tienen 3 bandas o tracks.

Los 3 tracks de las bandas magnéticas y su posición

El primer y segundo track llevan información redudante, de forma que si una banda está dañada puede leerse la otra, sin embargo no siguen el mismo formato.

Nombre del campo Longitud Uso
Start Sentinel (SS) 1 caracter Indica que comienza el track 1, siempre es %
Format Code (FC) 1 caracter Indica el tipo de tarjeta, B para crédito/débito
Primary Account Number (PAN) Hasta 19 digitos Normalmente, el número de la tarjeta que está impreso
Field Separator (FS) 1 caracter Separa el campo de longitud variable PAN del siguiente campo. Siempre es ^
Nombre Hasta 26 caracteres El nombre del titular de la tarjeta, con espacios si hace falta
Field Separator (FS) 1 caracter Separa el campo de longitud variable nombre del resto de campos. Siempre es ^
Expiration Date (ED) 4 dígitos Fecha de caducidad. Siempre en formato YYMM
Service Code (SC) 3 dígitos Indica que tipo de pagos son aceptados
Discretionary Data (DD) Indefinido, pero el track 1 solo puede almacenar 79 caracteres en total Este campo viene determinado por el fabricante de la tarjeta. Puede incluir datos como el CVV1 y PIN.
End Sentinel (ES) 1 caracter Se usa para indicar el fin del campo DD. Siempre es ?
Longitude Redundancy Check (LRC) 1 caracter Se usa para comprobar que se ha podido leer el track 1 entero sin problema

El track 2 fue desarrollado por la American Banking Association y es más corto.

Nombre del campo Longitud Uso
Start Sentinel (SS) 1 caracter Se usa para indicar el comienzo del track 2. Siempre es ;
PAN Hasta 19 caracteres Normalmente, el número de la tarjeta que aparece impreso
Field Separator (FS) 1 caracter Separa el campo de longitud variable PAN, siempre es =
Expiration Date (ED) 4 dígitos Fecha de caducidad en formato YYMM
Service Charge (SC) 3 dígitos Indica los pagos que pueden realizarse
Discretionary Data (DD) Indefinido, pero el track 2 no puede superar los 40 caracteres Campo determinado por el fabricante de la tarjeta. Puede incluir el CVV1 y PIN.
End Sentinel (ES) 1 caracter Indica que el track 2 acaba, siempre es ?
Longitude Redundancy Check (LRC) 1 caracter Usado para comprobar que el track 2 fue leído correctamente

El track 3 no se usa y habitualmente no contiene información. La única excepción se puede encontrar en Alemania donde el track 3 de las tarjetas de débito incluye la información necesaria para obtener la cuenta corriente sobre la que opera la tarjeta de débito.

Al pagar con estas tarjetas es habitual firmar, para dar nuestro consentimiento y en algunos países como España, era necesario mostrar el DNI o algún tipo de identificación.

El problema principal de estas tarjetas es su rápida y sencilla clonación. No deja de ser información que igual que es leída puede ser copiada y ningún lector sería capaz de distinguir entre una tarjeta original y una clonada. No obstante vamos a entrar más a fondo en la seguridad de estas tarjetas.

En primer lugar, toca hablar el CVV1. Se trata de un código que aunque el estándar ISO no exige, suelen llevar las tarjetas VISA y MasterCard. Se trata de un número generado en la emisión de la tarjeta de forma poco previsible fruto de la encriptación del número de la tarjeta y la fecha de caducidad. Las claves usadas en la encriptación, evidentemente, no son públicas. De este modo se consigue que una persona no pueda crear aleatoriamente tarjetas, ya que si bien puede acertar con el número y la fecha de caducidad, difícilmente lo hará con el CVV1. Este sistema de seguridad no protege en ningún momento contra los clonados totales.

También existe el CVV2. Se trata del código de 3 dígitos detrás de la tarjetas VISA / MasterCard (4 dígitos en American Express). Fue creado con la finalidad de mejorar la seguridad en las compras sin tarjeta o por Internet. El mecanismo de generación es similar al del CVV1, aunque está diseñado para que sea imposible que CVV1 y CVV2 coincidan. El CVV2 solo es usado en compras por Internet, y no está almacenado en la banda magnética. Tiene un propósito similar al CVV1 y es verificar que la tarjeta es realmente la emitida por el banco y no una generada aleatoriamente. Sin embargo, hay que advertir que el CVV2 no es un requisito para aceptar pagos por Internet (el CVV2 llegó a VISA en 2001, para aquel entonces Amazon ya era importante) y existen webs que admiten pagos con CVV2 incorrecto (como Stripe).

El PIN y el CVV2 tienen propósitos similares, si bien el PIN puede cambiarse y se deja para opeativas físicas, mientras que el CVV2 es inmutable.

Antiguamente los lectores de banda magnética no estaban conectadas a la red y al terminar el día los empleados realizaban todas las operaciones en lote. Con el paso del tiempo y la llegada de Electron y Maestro, los lectores de banda magnética empezaron a estar conectados a las redes EFTPOS.

Chip EMV

EMV hace referencia a Europay, MasterCard y VISA, y es que estas 3 compañías desarrollaron el sistema de chips más usado actualmente para los pagos. Este protocolo, definido en ISO/IEC 7816, incluye muchas ventajas respecto a la banda magnética. La principal diferencia es que si bien la banda magnética los datos estaban tal cual, en los chips EMV podemos incluir un procesamiento, gracias a un chip que se alimenta gracias al lector. Este estándar es todavía más complejo que el de banda magnética, debido a las mejoras que ha ido sufriendo con el paso de los años.

VCC es la entrada de corriente y GND es tierra. RST para resetear la comunicación, VPP para programar la memoria interna. CLK es la señal de reloj, I/O es para las comunicaciones (half-duplex). C4 y C8 son auxiliares y se pueden usar con dispositivos USB.

Uno de los objetivos de EMV fue ofrecer autenticación offline mucho más segura. Además EMV está diseñado para permitir múltiples apps que se ejecuten en el chip, aunque habitualmente las tarjetas solo llevan una app instalada. Aunque en Europa, los chips EMV se usan con PIN, no es estrictamente necesario y también pueden funcionar con firma.

En primer lugar, tenemos que hablar del sistema de autenticación. Todos los sistemas se basan en criptografía de clave pública-privada mediante RSA. El primer sistema es Static Data Authentication (SDA). Este sistema, el más rápido, no necesita que el chip sea capaz de realizar criptografía, sino que tiene los datos ya guardados de forma encriptada (con un criptograma o clave pública) y simplemente se leen. Cuando se realiza un pago, el lector realiza la verificación de la clave pública y si es correcta efectúa el pago. Como vemos, tampoco necesita conectarse a la red para realizar esta verificación, por lo que se ha añadido seguridad sin necesitar de estar conectado con el servidor. Ese sistema impide la modificación de los datos, no obstante es posible clonar enteramente una tarjeta sin ningún problema (copiaremos datos encriptados, pero eso da igual a fin de cuentas).

El segundo sistema, mucho más seguro, es Dynamic Data Authentication (DDA). Este sistema requiere que el chip sea capaz de realizar operaciones criptográficas. Por este motivo las tarjetas con este chip son más caras de emitir, aunque bajo los mandatos de VISA, MasterCard y SEPA, son las más emitidas en Europa desde 2011. La tarjeta en su interior contiene los datos y una clave privada. Cada vez que se realiza la verificación, la tarjeta responde con datos cifrados. Para ello, necesita que el terminal le pase los datos que necesita así como cierto número impredecible o aleatorio. De ese modo, las tarjetas clonadas no podrán responder lo mismo, ya que en realidad no poseen la clave privada del interior del chip. Este sistema permite verificar los PIN de forma segura sin conexión a la red interbancaria.

Flujo de trabajo de DDA

Existe un tercer sistema, denominado CDA o Combined Dynamic Data Authentication. Se trata de una mejora sobre DDA y es que DDA solo realiza la encriptación en el momento de la autorización, dejando el resto de la comunicación abierta. CDA aplica la encriptación a más partes de la comunicación.

RFID-NFC

Hoy en día las tarjetas que se emiten en España cuentan con esta tecnología. Estas tarjetas funcionan siguiendo el protocolo ISO/IEC 14443, en el cuál se definen las etiquetas RFID y los lectores RFID. Las etiquetas, al igual que los chips EMV, no necesitan energía eléctrica por si mismos.

Existen dos tipos de tarjetas contactless, las MSD y las EMV. Las MSD, solo usadas algún tiempo en Estados Unidos no son más que otra manera de leer la información de la banda magnética, con los problemas de seguridad que ya hemos comentado. Las tarjetas EMV sin embargo, siguen los protocolos del chip EMV. En realidad estas tarjetas suelen usar el mismo chip, solo que con dos vías de comunicación: el chip físico, y el RFID. El mayor problema del protocolo RFID original es que no es full-duplex, es decir, solo puede haber comunicación en un sentido. Para solventar este problema nació NFC, que permite comunicaciones bidireccionales. Sobre NFC se puede implementar EMV sin ningún problema.

La tarjeta de N26 es transparente y nos permite ver la antena necesaria para la comunicación

3DSecure

Los pagos online siguieron creciendo después de la introducción del CVV2 y aunque técnicamente está prohibido almacenar el CVV2 en una base de datos sumado al hecho de que no sea obligatorio para una transacción online, el fraude seguía aumentando. Por ello, VISA creó 3D-Secure, un protocolo basado en XML por el que es necesario introducir un código extra en una web propiedad del banco emisor para finalizar el pago. Este código puede ser de un solo uso, enviado por SMS o puede ser un PIN configurado de antemano.

Flujo de trabajo de 3D Secure

En 2016, ante la popularidad de este sistema, el consorcio EMVco lanzó 3DS 2.0, una versión mejorada del sistema, que entre otras cosas puede utilizarse para verificar la identidad de una persona sin necesidad de hacer un pago.

¿Tienes dudas? ¿Quiéres añadir o corregir algo? Usa los comentarios

La entrada Todo lo que debes sobre las tarjetas de crédito, débito y prepago aparece primero en Blog - Adrianistan.eu.

» Leer más, comentarios, etc...

xailer.info

Lanzamiento de Xailer 5.0.1

julio 27, 2017 10:50

Estimados usuarios de Xailer,

Hoy hemos publicado una nueva versión de Xailer que incorpora toda la documentación y resuelve algunos problemas de la primera versión 5.0.0.

Muchas gracias a todos los que habéis colaborado en la localización de problemas.

Podéis descargar la nueva versión desde el siguiente enlace:

http://www2.xailer.com/download/?es&file=1

Un cordial saludo
[El equipo de Xailer]

» Leer más, comentarios, etc...

Bitácora de Javier Gutiérrez Chamorro (Guti)

Uptime para DOS

julio 26, 2017 07:30

Al hablar de FAST, me entraron ganas de recordar viejos tiempos, así que un poco como con Bell/beep en ensamblador, decidí implementar algo sencillito que accediera al hardware de bajo nivel. Lo más tedioso de ensamblador, es que no tienes ninguna biblioteca que tenga implementadas conversiones de números para mostrarlos en pantalla (binario a ASCII), […]

La entrada Uptime para DOS aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

» Leer más, comentarios, etc...

Adrianistán

Timers en systemd, reemplazo a cron

julio 25, 2017 03:39

Odiado por muchos, alabado por otros, systemd se ha impuesto como el init por excelencia del mundo Linux. Las distros más populares como Debian, Ubuntu, Fedora, openSUSE y Arch Linux usan systemd por defecto. Hoy vengo a hablaros de una característica interesante de systemd que son son los timers. Se trata de poder definir que arranquen programas/servicios cada cierto tiempo o a una hora, su versatilidad es tal que pueden sustituir a cron en la mayoría de casos. Veamos como usar los timers.

Creando un servicio

Para crear un timer primero necesitamos crear un servicio simple que ejecute el comando que queramos al ser iniciado.

Crea el archivo miapp.service y ponlo en /etc/systemd/system/.

[Unit]
Description=Cat Mania GIF

[Service]
ExecStart=/home/pi/charlatan/catmania.py

 

Creando el timer

Para el timer es necesario un archivo con extensión timer en /etc/systemd/system/. Se suele usar el mismo nombre que el que tiene el servicio para escenarios simples. El archivo miapp.timer quedaría así:

[Unit]
Description=Run Cat Mania daily

[Timer]
OnCalendar=daily
RandomizedDelaySec=5min
Unit=catmania.service

[Install]
WantedBy=timers.target

 

Aquí es donde podemos realizar toda la magia de los timers. Los timers de systemd tienen dos modos de funcionamiento. En el primer modo, systemd ejecutará el timer cada cierto tiempo desde el arranque del sistema ( o algo más tarde si no queremos saturar el sistema). El otro modo es el modo calendario donde podemos especificar fechas y horas con wildcards y patrones. Además, es posible modificar la hora exacta de ejecución del timer con las propiedades de Randomize y que evitan que si hay varios timers configurados a la vez, no lo hagan exactamente a la vez (pero siempre dentro de un margen). Otras opciones para OnCalendar pueden ser:

OnCalendar=Wed,Sun *-*-10..15 12:00:00

En ese caso, el timer se activaría los miércoles y domingos que cayesen entre el 10 y el 15 de todos los meses a las 12 horas.

Activando el timer

Una vez estén copiados los archivos service y timer ejecutamos:

systemctl start miapp.timer
systemctl enable miapp.timer

Ya solo queda esperar a que systemd lance el servicio en la fecha y hora especificados.

Monitorización

Los timers están integrados con systemd, por lo que los logs se registran con journalctl. Podemos inspeccionarlos de forma sencilla:

journalctl -f -u miapp.service
journalctl -f -u miapp.timer

Para revisar los timers instalados y activos en el sistema puedes usar el comando:

systemtctl list-timers

Y ya estaría. De este modo podemos usar systemd como reemplazo de cron. Yo personalmente lo prefiero, ya que me parece una sintaxis más clara.

La entrada Timers en systemd, reemplazo a cron aparece primero en Blog - Adrianistan.eu.

» Leer más, comentarios, etc...

Una sinfonía en C#

¿Qué es un circuit breaker?

julio 24, 2017 09:13

En este post voy a contar de qué se trata el patrón circuit breaker para utilizar cuando dependemos de recursos externos y vamos a ver un ejemplo de implementación en C#.

Objetivo

El objetivo de este patrón es poder manejar un error al cosultar un servicio remoto que puede tomar un tiempo indeterminado en recupearse. La idea es mejorar la capacidad de nuestra aplicación a ser resilente a fallos.

Cuando nuestra aplicación consume un servicio externo puede ocurrir que este servicio sufra problemas momentaneos o que tengamos problemas de latencia o conectividad, en general estos problemas se pueden manejar utilizando el patrón retry, sin embargo si el problema no se resuelve rápidamente ésta no es siempre la mejor estrategia.

Entonces el circuit breaker evita que nuestra aplicación consulte un recurso externo (otra aplicación, un servicio, una base de datos, etc.) que muy probablemente nos de error por un tiempo hasta que se estabilice.

En resumen el circuit breaker se utilizará donde no tenga sentido seguir reintentando que el recurso externo responda y el patrón retry no tendía utilidad y por supuesto trasladar los errores causados por la falla del recurso externo (o la espera a que responda) a nuestra aplicación nos traería otro tipo de problemas como por ejemplo operaciones de que se quedan esperando y consumiendo hilos de ejecución, memoria y potencialmente causando más problemas a nuestra aplicación e incluso podamos empeorar la situación del servicio externo no dándole oportunidad de recuperarse si lo continuamos consultando.

Por último el circuit breaker actuará como un proxy sobre las operaciones que queramos manejar con él, esto por supuesto puede penalizar levemente la performance.

Implementando un circuit breaker con C#

Básicamente el circuit breaker es una máquina de estados que puede exponer los siguientes estados:

  • Open: El recurso no está disponible porque se alcanzó un número de fallas en cierto tiempo.
  • Half-Open: Estando en Open ha transcurrido un tiempo dado y pasa a este estado, si se puede acceder al recurso se pasa a Closed, si falla se vuelve a Open y se pone el contador en cero
  • Closed: el circuito funciona, en caso de detectarse una falla se incrementa un contador, si se alcanza un número determinado en un periodo de tiempo dado se cambiar a Open

Por otro lado tendremos al menos dos parámetros

  • Threshold: La cantidad de veces que aceptamos que el recurso externo de error antes de considerarlo en problemas.
  • Reset timeout: El tiempo que se esperará antes que volver a intentar consultar el servicio (el tiempo que le daremos a que se recupere)

 

image

Implemetación simple en C#

Acá dejo una implementación simple den C# utilizando una máquia de estados clásica, esto se puede evolucionar mucho por supuesto.

public interface ICircuitBreaker
{
    /// <summary>
    /// Obtiene la cantidad de fallas aceptadas hasta pasar a estado Open
    /// </summary>
    int Threshold { get; }

    /// <summary>
    /// Obtine el tiempo de espera para pasar a estado Half-Open
    /// </summary>

    TimeSpan Timeout { get; }
    /// <summary>
    /// Obtiene el estado actual
    /// </summary>

    CircuitBreakerState CurrentState { get; }
    /// <summary>
    /// Permite invocar una acción utilizado el circuit breaker
    /// </summary>
    /// <param name="protectedCode">La acción que será invocada</param>
    /// <returns>El estado resultante</returns>
    CircuitBreakerState AttemptCall(Action protectedCode);
}

Esta sería la interfaz de nuestro circuit breaker, es sencilla y se puede mejorar mucho pero sirve para el ejemplo, vamos a la implementación

public enum CircuitBreakerState
{
    Close,
    Open,
    HalfOpen
}

public class SimpleCircuitBreaker : ICircuitBreaker
{
    private int threshold;
    private TimeSpan timeout;
    private int attemptCounter;
    private DateTime failureTime;
    private CircuitBreakerState currentState = CircuitBreakerState.Close;

    public int Threshold => this.threshold;
    public TimeSpan Timeout => this.timeout;
    public CircuitBreakerState CurrentState => this.currentState;

    public SimpleCircuitBreaker(int threshold, TimeSpan timeout)
    {
        this.threshold = threshold;
        this.timeout = timeout;
    }

    public CircuitBreakerState AttemptCall(Action protectedCode)
    {
        switch (this.currentState)
        {
            case CircuitBreakerState.Close:

                try
                {
                    protectedCode();
                }
                catch (Exception)
                {
                    this.attemptCounter++;
                    if (this.attemptCounter > this.threshold)
                    {
                        this.failureTime = DateTime.Now;
                        this.attemptCounter = 0;
                        this.currentState = CircuitBreakerState.Open;
                    }
                }

                break;
            case CircuitBreakerState.Open:
                if (this.failureTime.Add(this.timeout) > DateTime.Now)
                {
                    this.currentState = CircuitBreakerState.HalfOpen;
                }
                break;
            case CircuitBreakerState.HalfOpen:
                try
                {
                    protectedCode();
                }
                catch (Exception)
                {
                    this.failureTime = DateTime.Now;
                    this.attemptCounter = 0;
                    this.currentState = CircuitBreakerState.Open;
                }
                break;
        }
        return this.currentState;
    }
}

La forma de utilizar el código es crear una instancia del SimpleCircuitBreaker y llamar al recurso externo a través del método AttenptCall.

Como vemos una máquina de estados que inicia en Closed y en caso de error incrementa un contador, si se llega el número de fallos permitos (threshold) pasa a Open, si se ha pasa un tiempo dado (el que consideramos que es adecuado para que el recurso externo se recupere) se pasa al Half-Open, si se intenta acceder nuevamente y falla se vuelve a Open, si funciona a Close.

De esta manera todo el código que dependa de recursos externos puede conocer el estado del recurso y esperar para llamarlo en caso que se encuentre en fallo.

Como dije se puede mejorar mucho este código no tiene sentido para explicar el concepto hacerlo ahora.

Les dejo un link con el código en github.

Nos leemos.

» Leer más, comentarios, etc...

Poesía Binaria

Obtener información básica sobre procesos del sistema Linux en C y C++ (parte 3)

julio 24, 2017 08:11

procesos del sistema

Cuando te independizas y te vas a vivir a un piso o a una casa te preguntas, ¿cómo serán mis vecinos? Por un lado tendremos al típico que deja la basura en la puerta de su casa durante todo el día y esparce olores al resto de los vecinos; o el que desea compartir la música que escucha con los demás y el que cuando entra al edificio y ve que vas a entrar va corriendo al ascensor para no esperarte… Aunque como procesos en ejecución en un entorno Linux muchas veces queremos saber cómo son nuestros vecinos y, al contrario de lo que puede parecer en una comunidad de vecinos, éstos suelen ser mucho más receptivos y dispuestos a darnos información.

Podemos averiguar cuántos procesos hay en ejecución, la memoria o el tiempo de CPU que consumen, PID, PID del padre, threads, usuario y grupos real, usuario efectivo, puntuación OOM, estado actual y mucho más. Y cómo no, accediendo simplemente a archivos del sistema, lo cual nos proporciona una manera muy fácil para acceder independientemente del lenguaje que utilicemos.

Conceptos básicos

Cada vez que se ejecuta un proceso, el sistema operativo nos deja ver información sobre él a través de un directorio (virtual, no está escrito en disco ni nada) llamado /proc/[PID] donde PID es el identificador del proceso o Process ID y éste es un número entre 1 y el valor que podemos ver en /proc/sys/kernel/pid_max. En mi caso:

cat /proc/sys/kernel/pid_max
32768

Por lo tanto, en mi sistema el número máximo que puede tener un PID y, por tanto, el número máximo de procesos que puede haber al mismo tiempo en el sistema es de 32768. Podemos aumentar este número si queremos escribiendo sobre /proc/sys/kernel/pid_max o con:
sudo sysctl kernel.pid_max=65536

El sistema operativo y las aplicaciones que corren sobre él deberán utilizar dicho PID para referirse a un proceso concreto. Por ejemplo el sistema operativo deberá almacenar información relativa a la ejecución del proceso cada vez que necesite memoria, realice eventos de entrada/salida o simplemente pausar y reanudar su ejecución. Otras aplicaciones deberán utilizar este PID para comunicarse con el proceso (por ejemplo mediante señales) o si queremos saber quién es el que más memoria está comiendo.

Información básica del proceso

Para lo que queremos hacer, tendremos que leer el fichero /proc/PID/statm. Esto lo podemos hacer con la línea de comando, por ejemplo buscaremos el proceso emacs (como hay varios, cogeremos el primero y consultaremos información):

pidof emacs
23406 10997 7345
cat /proc/23406/stat
23406 (emacs) S 30548 23406 30548 34835 30548 4194304 53536 589 224 13 1762 284 0 0 20 0 4 0 161959260 731086848 49501 18446744073709551615 4194304 6539236 140735377727232 140735377722352 139924333532780 0 0 67112960 1535209215 0 0 0 17 1 0 0 52 0 0 8637344 21454848 51675136 140735377731945 140735377731964 140735377731964 140735377735657 0

Y en todos los números que vemos en este momento, vamos a poner un cierto orden definiendo cada uno de ellos y el tipo de variable con el que podemos almacenarlo:
  • pid (número, int) – Identificador del proceso.
  • comm (cadena, char [32]) – Nombre del ejecutable entre paréntesis.
  • state (carácter, char) – R (en ejecición), S (bloqueado), D (bloqueo ininterrumpible), Z (zombie), T (ejecución paso a paso), W (transfiriendo páginas).
  • ppid (número, int) – Pid del padre.
  • pgrp (número, int) – Identificador del grupo de procesos.
  • session (número, int) – Identificador de la sesión.
  • tty_nr (número, int) – Terminal que usa el proceso.
  • tpgid (número, int) – Identificador del grupo de procesos del proceso terminal al que pertenece el proceso actual.
  • flags (número natural, unsigned) – Flags del proceso actual.
  • minflt (número natural largo, unsigned long) – Fallos de página menores, que no han necesitado cargar una página de memoria desde disco.
  • cminflt (número natural largo, unsigned long) – Fallos de página menores que han hecho los procesos hijo del proceso actual.
  • majflt (número natural largo, unsigned long) – Fallos de página mayores, que han necesitado cargar una página de memoria desde disco.
  • cmajflt (número natural largo, unsigned long) – Fallos de página mayores que han hecho los procesos hijo del proceso actual.
  • utime (número natural largo, unsigned long) – Tiempo que este proceso ha estado en ejecución en modo usuario. Se mide en ticks de reloj o jiffies.
  • stime (número natural largo, unsigned long) – Tiempo que este proceso ha estado en modo kernel.
  • cutime (número largo, long) – Tiempo que los hijos de este proceso han estado en ejecución en modo usuario.
  • cstime (número largo, long) – Tiempo que los hijos de este proces han estado en ejecución en modo kernel.
  • priority (número largo, long) – Prioridad del proceso. Depende del planificador de procesos activo.
  • nice (número largo, long) – Es la simpatía del proceso. Este valor va de 19 (prioridad baja o generosidad, porque el proceso intenta no consumir mucho) a -20 (prioridad alta o codicia, porque el proceso intenta acaparar CPU).
  • num_threads (número largo, long) – Número de hilos que tiene este proceso (mínimo 1).
  • itrealvalue (número largo, long) – No se usa desde el kernel 2.6.17, ahora siempre vale 0.
  • start_time (número natura largo largo, unsigned long long) – Momento en el que el proceso empezó. Se mide en ticks de reloj desde el arranque del equipo.
  • vsize (número natural largo, unsigned long) – Tamaño ocupado en memoria virtual en bytes.
  • rss (número largo, long) – Páginas que tiene el proceso en memoria RAM actualmente.
  • rsslim (número natural largo, unsigned long) – Límite en bytes del rss del proceso.
  • startcode (número largo, long) – Dirección de memoria donde empieza el código del programa.
  • endcode (número largo, long) – Dirección de memoria donde acaba el código del programa.
  • startstack (número largo, long) – Dirección de memoria donde empieza la pila (pero como la pila va al revés, será la parte de abajo de la pila.
  • kstkesp (número largo, long) – Posición actual del puntero de pila.
  • kstkeip (número largo, long) – Posición actual del puntero de instrucciones.
  • signal (número largo, long) – Obsoleto, se usa información procedente de /proc/PID/status.
  • blocked (número largo, long) – Obsoleto, se usa información procedente de /proc/PID/status.
  • sigignore (número largo, long) – Obsoleto, se usa información procedente de /proc/PID/status.
  • sigcatch (número largo, long) – Obsoleto, se usa información procedente de /proc/PID/status.
  • wchan (número natural largo, unsigned long) – Canal de espera dentro del kernel.
  • nswap (número natural largo, unsigned long) – Número de páginas en memoria swap.
  • cnswap (número natural largo, unsigned long) – Número de páginas en memoria swap de los procesos hijo.
  • exit_signal (número, int) – Señal que se envierá al padre cuando finalice este proceso.
  • processor (número, int) – ID de CPU donde se ejecutó este proceso por última vez.
  • rt_priority (número sin signo, unsigned) – Un número entre 1 y 99 si es un proceso de tiempo real, 0 si no.
  • policy (número sin signo, unsigned) – Política de planificación.
  • delayacct_blkio_ticks (número natural largo largo, unsigned long long) – Retrasos añadidos en ticks de reloj.
  • guest_time (número natural largo, unsigned long) – Tiempo invertido corriendo una CPU virtual.
  • cguest_time (número largo, long) – Tiempo invertido corriendo una CPU virtual por los procesos hijo.
  • start_data (número natural largo, unsigned long) – Dirección de memoria donde empieza el área de datos.
  • end_data (número natural largo, unsigned long) – Dirección de memoria donde acaba el área de datos.
  • start_brk (número natural largo, unsigned long) – Dirección de posible expansión del segmento de datos.
  • arg_start (número natural largo, unsigned long) – Dirección de memoria donde se encuentran los argumentos del programa (argv).
  • arg_end (número natural largo, unsigned long) – Dirección de memoria donde terminan los argumentos del programa (argv).
  • env_start (número natural largo, unsigned long) – Dirección donde empiezan las variables de entorno del programa.
  • env_end (número natural largo, unsigned long) – Dirección donde terminan las variables de entorno del programa.
  • exit_code (número, int) – Código de salida del thread.

Tenemos mucha información que seguramente no necesitemos, y tenemos que leerla. Afortunadamente, casi todo son números y en C podemos hacer algo rápido con scanf.

Obteniendo datos de un proceso en C

El código es algo largo por la cantidad de datos que leo, y eso que sólo leo hasta el rss y presento en pantalla algunos datos menos. Sólo quiero que tengamos una primera aproximación. Debemos tener en cuenta que aunque accedamos a la información como si fueran ficheros, en realidad no tienen que ver nada con el disco. Ni están en disco, ni gastarán tiempo de entrada/salida. Todo debería ser muy rápido porque al final estamos haciendo un par de llamadas al sistema operativo. Para el ejemplo he utilizado fscanf, porque es muy sencillo hacer la lectura y el parseo, aunque habrá algún que otro detalle (o mejor dicho, problema).

Por otro lado, dado lo pequeños que son los archivos también podemos almacenarlos por completo en un buffer y leer de nuestro buffer por si queremos utilizar otro método de lectura y parseo.

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
#include <unistd.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    if (argc<2)
        return perror("Falta un argumento"), 1;

    char statFileName[128];             /* /proc/PIC/stat - I think 512 bytes is far enough */
   
    sprintf(statFileName, "/proc/%s/stat", argv[1]);
    /* Podíamos comprobar que argv[1] es numérico y que es de poco
       tamaño, pero para el ejemplo nos vale. */

    FILE *fd = fopen(statFileName, "r");
    if (fd == NULL)
        return perror("No puedo encontrar el proceso especificado"),1;
    char
    state,
      name[32];
    int
      pid,
      ppid,
      pgrp,
      session,
      tty,
      tpgid,
      nlwp;

    unsigned long
    flags,
      min_flt,
      cmin_flt,
      maj_flt,
      cmaj_flt,
      vsize;

    unsigned long long
    utime,
      stime,
      cutime,
      cstime,
      start_time;

    long
    priority,
      nice,
      alarm,
      rss;
   
    fscanf(fd, "%d %s "
                 "%c "
                 "%d %d %d %d %d "
                 "%lu %lu %lu %lu %lu "
                 "%Lu %Lu %Lu %Lu "
                 "%ld %ld "
                 "%d "
                 "%ld "
                 "%Lu "
                 "%lu "
                 "%ld",
                 &pid,
                 name,
                 &state,
                 &ppid, &pgrp, &session, &tty, &tpgid,
                 &flags, &min_flt, &cmin_flt, &maj_flt, &cmaj_flt,
                 &utime, &stime, &cutime, &cstime,
                 &priority, &nice,
                 &nlwp,
                 &alarm,
                 &start_time,
                 &vsize,
                 &rss);
   
    fclose(fd);

    printf ("PID: %d\n"
                    "CMD: %s\n"
                    "Estado: %c\n"
                    "PPID: %d\n"
                    "Tiempo usuario: %Lu\n"
                    "Tiempo kernel: %Lu\n"
                    "Nice: %ld\n"
                    "Threads: %d\n"
                    "Iniciado en: %Lu\n"
                    "Tamaño: %lu\n",
                    pid, name, state, ppid, utime, stime, nice, nlwp, start_time, vsize);
}

El resultado será algo como:

./procesos 4971
PID: 4971
CMD: (firefox)
Estado: S
PPID: 4776
Tiempo usuario: 642512
Tiempo kernel: 41987
Nice: 0
Threads: 60
Iniciado en: 23836769
Tamaño: 5043314688

Sí, el tamaño es ese, firefox ahora mismo me está cogiendo 5Gb de memoria virtual (seguro que dentro de 5 años leeremos esto y parecerá utópico).

Esta información está muy bien, pero no me dice mucho. Por un lado, el tiempo de usuario y kernel tenemos que traducirlo y hacer algo con él, por ejemplo, calcular el % de CPU utilizado, poner la fecha de inicio y el tamaño en formato humano y vamos a quitar los paréntesis al nombre del proceso añadiendo algo más de control de errores, aunque esto último no es estrictamente necesario).

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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <sys/sysinfo.h>

/* Presenta un intervalo de tiempo (en segundos) en horas:minutos:segundos */
char* timeInterval(char* buffer, size_t bufferSize, unsigned long seconds);
/* Presenta la fecha en formato humano */
char* humanSize(char* buffer, size_t bufferSize, long double size, short precission);
/* Error fatal! */
void panic(char* error);
/* Obtiene uptime del sistema */
long uptime();

int main(int argc, char* argv[])
{
    if (argc<2)
        panic("Falta un argumento");

    char statFileName[128];             /* /proc/PIC/stat - I think 512 bytes is far enough */
   
    sprintf(statFileName, "/proc/%s/stat", argv[1]);
    /* Podíamos comprobar que argv[1] es numérico y que es de poco
       tamaño, pero para el ejemplo nos vale. */

    FILE *fd = fopen(statFileName, "r");
    if (fd == NULL)
        panic("No puedo encontrar el proceso especificado");
   
    char
    state,
      name[32];
    int
      pid,
      ppid,
      pgrp,
      session,
      tty,
      tpgid,
      nlwp;
    double
        pcpu;

    unsigned long
    flags,
      min_flt,
      cmin_flt,
      maj_flt,
      cmaj_flt,
      vsize;

    unsigned long long
    utime,
      stime,
      cutime,
      cstime,
      start_time;

    long
    priority,
      nice,
      alarm,
      rss;

    char buffer[512];
    fgets(buffer, 512, fd);
    char* cstart=strchr(buffer, '('); /* Cogemos el primer ( de la cadena */
    char* cend  =strrchr(buffer, ')'); /* Cogemos el último ) de la cadena */
    strncpy(name, cstart+1, (cend-cstart<33)?cend-cstart-1:32); /* Necesitamos delimitar el nombre a 32 caracteres (por el tamaño de nuestro buffer */
    if ( (cstart == NULL) || (cend == NULL) )
        panic("No se pudo determinar el nombre del proceso");
   
    sscanf(buffer, "%d", &pid);
    sscanf(cend+2, "%c "                    /* +2 para eliminar el ) y el espacio siguientes */
                 "%d %d %d %d %d "
                 "%lu %lu %lu %lu %lu "
                 "%Lu %Lu %Lu %Lu "
                 "%ld %ld "
                 "%d "
                 "%ld "
                 "%Lu "
                 "%lu "
                 "%ld",
                 &state,
                 &ppid, &pgrp, &session, &tty, &tpgid,
                 &flags, &min_flt, &cmin_flt, &maj_flt, &cmaj_flt,
                 &utime, &stime, &cutime, &cstime,
                 &priority, &nice,
                 &nlwp,
                 &alarm,
                 &start_time,
                 &vsize,
                 &rss);
   
    fclose(fd);

    long ticks_sec = sysconf (_SC_CLK_TCK); /* Ticks de reloj por segundo */
    char utimeStr[32], stimeStr[32], start_timeStr[32], vsizeStr[32], startedStr[32];

    /* Calculamos cuándo se inició el proceso */
    struct tm starttm;
    long now = time(NULL);
    time_t started = now - uptime() + start_time/ticks_sec;
    strftime (startedStr, 32, "%d/%m/%Y %H:%M:%S", localtime_r(&started, &starttm));
    /* Como stat nos da segundos*ticks_sec desde que se arrancó el ordenador tendremos
         que operar con el uptime del ordenador y con la fecha y hora actual para
         averiguarlo. */


    unsigned long long procuptime = uptime() - start_time / ticks_sec;
    unsigned long long total_time =  utime + stime;     /* Tiempo total en ejecución. */
    pcpu = (double)total_time / procuptime;
   
    printf ("PID: %d\n"
                    "CMD: %s\n"
                    "Estado: %c\n"
                    "PPID: %d\n"
                    "Tiempo usuario: %s\n"
                    "Tiempo kernel: %s\n"
                    "Nice: %ld\n"
                    "Threads: %d\n"
                    "Iniciado hace: %s\n"
                    "Iniciado el: %s\n"
                    "Tamaño: %s\n"
                    "%% CPU: %lf\n",
                    pid, name, state, ppid,
                    timeInterval(utimeStr, 32, utime/ticks_sec),
                    timeInterval(stimeStr, 32, stime/ticks_sec),
                    nice, nlwp,
                    timeInterval(start_timeStr, 32, uptime() - start_time/ticks_sec),
                    startedStr,
                    humanSize(vsizeStr, 32, vsize, -1),
                    pcpu);
}

char* timeInterval(char* buffer, size_t bufferSize, unsigned long seconds)
{
    int hours = seconds / 3600,
        rem = seconds % 3600,
        minutes = rem / 60,
        secs = rem % 60;
   
    snprintf (buffer, bufferSize, "%d:%d:%d", hours, minutes, secs);
   
    return buffer;
}

char* humanSize(char* buffer, size_t bufferSize, long double size, short precission)
{
    static const char* units[10]={"bytes","Kb","Mb","Gb","Tb","Pb","Eb","Zb","Yb","Bb"};

    char format[10];

    int i= 0;

    while (size>1024) {
        size = size /1024;
        i++;
    }

    if (precission < 0)
        precission=3;

    snprintf(format, 10, "%%.%dLf%%s", precission);
    snprintf(buffer, bufferSize, format, size, units[i]);

    return buffer;
}

long uptime()
{
    struct sysinfo si;
    if (sysinfo(&si) <0)
        panic("No puedo obtener sysinfo()");
   
    long tmp = si.uptime;
    return tmp;
}

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

Vaya, ¡qué montón de código! Aunque en parte se parece al anterior. En principio se han añadido funciones para representar el tamaño en memoria en formato humano, para representar fechas e intervalos de manera legible y para calcular el tiempo que lleva encendido el ordenador, o uptime, necesario para algunos cálculos que veremos más adelante.

Veamos el resultado de la ejecución de este programa, con firefox, como antes:

./procesos 4971
PID: 4971
CMD: firefox
Estado: S
PPID: 4776
Tiempo usuario: 11:37:0
Tiempo kernel: 0:32:32
Nice: 0
Threads: 63
Iniciado hace: 24:6:45
Iniciado el: 19/07/2017 21:28:00
Tamaño: 5.088Gb
% CPU: 50.000000

Una pequeña consideración sobre los tiempos de usuario y kernel, así como del tiempo total que lleva el proceso. Por un lado, el uptime del proceso, el tiempo que lleva arrancado es la hora actual menos la hora a la que arrancó el proceso. Es decir, si cargué el programa hace 10 minutos, el programa lleva 10 minutos encendido (es una tontería decirlo así, pero se puede confundir más adelante). Por otro lado tenemos los tiempos de kernel y de usuario, que es el tiempo total que el proceso ha hecho algo realmente; el tiempo de usuario podemos considerarlo computación propia del proceso, cuando está parseando un texto, creando ventanas, botones, ejecutando Javascript, etc; en otro plano tenemos el tiempo de kernel, que es el tiempo que el núcleo del sistema operativo ha gastado en el proceso; es decir, el proceso pedirá cosas al sistema operativo, como la lectura de un archivo, acceso a dispositivos, etc.

Eso sí, puede (y es lo más normal) que la suma de los tiempos de kernel y usuario no sea ni por asomo el uptime del proceso. Y está bien, eso es que el proceso ha habido momentos que no ha estado haciendo nada (ha estado en modo Sleeping) y no ha necesitado CPU.

Y veamos los cálculos que se han hecho:

  • Para expresar el tiempo de usuario en segundos, tenemos que dividirlo por los ticks de reloj por segundo. Suelen ser 100 en la mayoría de los sistemas, es una configuración del kernel, pero podemos averiguarlo consultando sysconf (_SC_CLK_TCK). Luego ese intervalo lo transformaremos en horas:minutos:segundos.
  • Para saber cuánto hace que se inició el proceso. Utilizaremos start_time, pero claro, este valor indica cuántos ticks pasaron desde que se arrancó el ordenador hasta que se inició el proceso. Por un lado sabemos transformar de ticks a segundos. Así que restaremos al uptime actual el start_time en segundos.
  • Para saber el momento en el que se inició el proceso. Teniendo claro el punto anterior, éste es fácil. Sólo que tenemos que saber la fecha y hora actuales, que las sacamos en formato UNIX, así que le restamos el uptime y luego le sumamos el start_time…
  • Porcentaje de CPU. ¡Cuidado! Es el porcentaje de CPU total del proceso durante toda su vida. Consideraremos el porcentaje de CPU como la relación entre el tiempo que ha estado el proceso utilizando la CPU y el tiempo total que lleva el proceso arrancado. Es decir, si el proceso lleva arrancado 10 minutos y la suma de tiempos de usuario y kernel es de 2 minutos. El proceso ha consumido un 20% de CPU. Eso sí, puede ser que la suma de los tiempos de kernel y usuario sea mayor que el tiempo que lleva el proceso arrancado. Esto sucederá en aplicaciones multihilo, ya que los tiempos de kernel y usuario están calculados para un sólo núcleo de CPU. Si el proceso ha utilizado 2 núcleos al 100% durante todo el tiempo de vida del proceso, la suma de los tiempos anteriormente comentados será el doble.

No me convence mucho el % de CPU…

Normal, a mí tampoco. Hasta ahora lo hemos calculado de forma absoluta. Es decir, desde que se inició el proceso hasta ahora. Claro, puede que cuando arrancó el proceso utilizara ocho núcleos durante un minuto (8 minutos de tiempo de kernel+usuario) y haya estado 9 minutos casi sin hacer nada. Si ejecutamos top ni vemos el proceso, pero mi aplicación piensa que el proceso se come un 80% de CPU.

Para hacer el cálculo más preciso debemos centrarnos en un intervalo de tiempo. Así, calcularemos los tiempos de kernel+usuario en un momento del tiempo, esperaremos un par de segundos, y luego calculamos de nuevo, hacemos la diferencia y dividimos por el intervalo. Así que en lugar de sacar el porcentaje de CPU desde el principio, calcularemos el porcentaje de CPU para el intervalo que acaba de suceder. De esta forma tendremos una medida más precisa, y si os fijáis, es lo que hace top, espera un tiempo y vuelve a hacer el cálculo.
Para ello tendremos que volver a leer el archivo y volver a parsear stat, por eso en el siguiente ejemplo vais a ver un struct con toda la información y una función que la extrae (un paso más en el programa):

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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <sys/sysinfo.h>

struct ProcessInfo
{
        char
    state,
      name[32];
    int
      pid,
      ppid,
      pgrp,
      session,
      tty,
      tpgid,
      nlwp;

    unsigned long
    flags,
      min_flt,
      cmin_flt,
      maj_flt,
      cmaj_flt,
      vsize;

    unsigned long long
    utime,
      stime,
      cutime,
      cstime,
      start_time;

    long
    priority,
      nice,
      alarm,
      rss;
};
/* Presenta un intervalo de tiempo (en segundos) en horas:minutos:segundos */
char* timeInterval(char* buffer, size_t bufferSize, unsigned long seconds);
/* Presenta la fecha en formato humano */
char* humanSize(char* buffer, size_t bufferSize, long double size, short precission);
/* Error fatal! */
void panic(char* error);
/* Obtiene uptime del sistema */
long uptime();

struct ProcessInfo getProcessInfo(int pid);

int main(int argc, char* argv[])
{
    if (argc<2)
        panic("Falta un argumento");

    double pcpu;
   
    struct ProcessInfo pi1 = getProcessInfo(atoi(argv[1]));

    long ticks_sec = sysconf (_SC_CLK_TCK); /* Ticks de reloj por segundo */
    char utimeStr[32], stimeStr[32], start_timeStr[32], vsizeStr[32], startedStr[32];

    /* Calculamos cuándo se inició el proceso */
    struct tm starttm;
    long now = time(NULL);
    time_t started = now - uptime() + pi1.start_time/ticks_sec;
    strftime (startedStr, 32, "%d/%m/%Y %H:%M:%S", localtime_r(&started, &starttm));
    /* Como stat nos da segundos*ticks_sec desde que se arrancó el ordenador tendremos
         que operar con el uptime del ordenador y con la fecha y hora actual para
         averiguarlo. */


    unsigned long long total_time_s =  pi1.utime + pi1.stime;   /* Tiempo total en ejecución. */
    sleep(3);
    struct ProcessInfo pi2 = getProcessInfo(atoi(argv[1]));
    unsigned long long total_time_e =  pi2.utime + pi2.stime;   /* Tiempo total en ejecución. */
   
    pcpu = (double)(total_time_e - total_time_s) / 3;
   
    printf ("PID: %d\n"
                    "CMD: %s\n"
                    "Estado: %c\n"
                    "PPID: %d\n"
                    "Tiempo usuario: %s\n"
                    "Tiempo kernel: %s\n"
                    "Nice: %ld\n"
                    "Threads: %d\n"
                    "Iniciado hace: %s\n"
                    "Iniciado el: %s\n"
                    "Tamaño: %s\n"
                    "%% CPU: %lf\n",
                    pi1.pid, pi1.name, pi1.state, pi1.ppid,
                    timeInterval(utimeStr, 32, pi1.utime/ticks_sec),
                    timeInterval(stimeStr, 32, pi1.stime/ticks_sec),
                    pi1.nice, pi1.nlwp,
                    timeInterval(start_timeStr, 32, uptime() - pi1.start_time/ticks_sec),
                    startedStr,
                    humanSize(vsizeStr, 32, pi1.vsize, -1),
                    pcpu);
}

char* timeInterval(char* buffer, size_t bufferSize, unsigned long seconds)
{
    int hours = seconds / 3600,
        rem = seconds % 3600,
        minutes = rem / 60,
        secs = rem % 60;
   
    snprintf (buffer, bufferSize, "%d:%d:%d", hours, minutes, secs);
   
    return buffer;
}

char* humanSize(char* buffer, size_t bufferSize, long double size, short precission)
{
    static const char* units[10]={"bytes","Kb","Mb","Gb","Tb","Pb","Eb","Zb","Yb","Bb"};

    char format[10];

    int i= 0;

    while (size>1024) {
        size = size /1024;
        i++;
    }

    if (precission < 0)
        precission=3;

    snprintf(format, 10, "%%.%dLf%%s", precission);
    snprintf(buffer, bufferSize, format, size, units[i]);

    return buffer;
}

long uptime()
{
    struct sysinfo si;
    if (sysinfo(&si) <0)
        panic("No puedo obtener sysinfo()");
   
    long tmp = si.uptime;
    return tmp;
}

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

struct ProcessInfo getProcessInfo(int pid)
{
    char statFileName[128];             /* /proc/PIC/stat - I think 512 bytes is far enough */

    struct ProcessInfo pi;
   
    sprintf(statFileName, "/proc/%d/stat", pid);
    FILE *fd = fopen(statFileName, "r");
    if (fd == NULL)
        panic("No puedo encontrar el proceso especificado");
   

    char buffer[512];
    fgets(buffer, 512, fd);
    char* cstart=strchr(buffer, '('); /* Cogemos el primer ( de la cadena */
    char* cend  =strrchr(buffer, ')'); /* Cogemos el último ) de la cadena */
    size_t namesize = (cend-cstart<33)?cend-cstart-1:32;
    strncpy(pi.name, cstart+1, namesize); /* Necesitamos delimitar el nombre a 32 caracteres (por el tamaño de nuestro buffer */
    pi.name[namesize]='\0';
    if ( (cstart == NULL) || (cend == NULL) )
        panic("No se pudo determinar el nombre del proceso");
   
    sscanf(buffer, "%d", &pi.pid);
    sscanf(cend+2, "%c "                    /* +2 para eliminar el ) y el espacio siguientes */
                 "%d %d %d %d %d "
                 "%lu %lu %lu %lu %lu "
                 "%Lu %Lu %Lu %Lu "
                 "%ld %ld "
                 "%d "
                 "%ld "
                 "%Lu "
                 "%lu "
                 "%ld",
                 &pi.state,
                 &pi.ppid, &pi.pgrp, &pi.session, &pi.tty, &pi.tpgid,
                 &pi.flags, &pi.min_flt, &pi.cmin_flt, &pi.maj_flt, &pi.cmaj_flt,
                 &pi.utime, &pi.stime, &pi.cutime, &pi.cstime,
                 &pi.priority, &pi.nice,
                 &pi.nlwp,
                 &pi.alarm,
                 &pi.start_time,
                 &pi.vsize,
                 &pi.rss);
   
    fclose(fd);
    return pi;
}

Línea de comandos y variables de entorno

¿Qué argumentos se han pasado al programa cuando se ejecutó? Si el programa está hecho en C, éste utilizará los famosos argc y argv para acceder a ellos. Pero, ¿un programa externo podrá acceder a esta información? Es lo que nos muestra ps cuando lo llamamos así (centrados en el PID que estamos consultando en este post):

ps ax -o pid,cmd | grep 4971
4971 /usr/lib/firefox/firefox -ProfileManager
24416 grep --color=auto 4971

Como vemos, yo ejecuté firefox con el argumento -ProfileManager, ¿cómo podemos consultarlo? Esta información está en /proc/PID/cmdline y encontraremos los argumentos separados por el carácter terminador, para que esta información sea mucho más fácil de manejar y procesar internamente. Veamos un 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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>


/* Error fatal! */
void panic(char* error);

int main(int argc, char* argv[])
{
    if (argc<2)
        panic("Falta un argumento");

    char cmdlineFileName[128];              /* /proc/PID/cmdline - I think 128 bytes is far enough */

    sprintf(cmdlineFileName, "/proc/%s/cmdline", argv[1]);
    FILE *fd = fopen(cmdlineFileName, "r");
    if (fd == NULL)
        panic("No puedo encontrar el proceso especificado");
  char *arg = 0;
    size_t argsize = 0;
    while(getdelim(&arg, &argsize, '\0', fd) != -1)
        {
      printf ("Argumento: %s\n", arg);
        }
    if (arg)
        free(arg);

    fclose(fd);
}

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

Si lo ejecutamos, veremos algo parecido a esto:

./cmdline 4971
Argumento: /usr/lib/firefox/firefox
Argumento: -ProfileManager
./cmdline 2526
Argumento: /usr/sbin/dnsmasq
Argumento: --no-resolv
Argumento: --keep-in-foreground
Argumento: --no-hosts
Argumento: --bind-interfaces
Argumento: --pid-file=/var/run/NetworkManager/dnsmasq.pid
Argumento: --listen-address=127.0.1.1
Argumento: --cache-size=0
Argumento: --conf-file=/dev/null
Argumento: --proxy-dnssec
Argumento: --enable-dbus=org.freedesktop.NetworkManager.dnsmasq
Argumento: --conf-dir=/etc/NetworkManager/dnsmasq.d

Como siempre, el primer argumento coincide con el ejecutable del programa y los siguientes serán los argumentos que hemos pasado para modificar su comportamiento. En este ejemplo los imprimimos directamente en pantalla, pero ya está en vuestra mano otro tipo de análisis: almacenarlo en arrays o en listas enlazadas, buscar un argumento en particular, mirar si se ha colado un password (que hay programadores a los que se les pasa esto…), o lo que queráis.

Del mismo modo, pero cambiando el archivo por /proc/PID/environ podemos extraer el valor de las variables de entorno del programa. Y puede haber información muy interesante acerca de la ejecución bajo un entorno de escritorio, sesión SSH (si se ejecutó en remoto), idioma, directorios, informes de errores y demás. Incluso muchos programas, para ejecutarlos utilizan un script lanzador que establece los valores de ciertas variables antes de lanzar la ejecución del binario y lo podemos ver aquí.

Más información de estado del proceso

¿Queremos más información del proceso? Pues miremos /proc/pid/status. Muy parecido a /proc/pid/stat, con la información en un lenguaje más inteligible, incluso con algunos datos más que pueden resultar interesantes como por ejemplo:

  • Cpus_allowed, Cpus_allowd_list: Para saber las CPUs pueden ejecutar código de este proceso
  • voluntary_ctxt_switches y nonvoluntary_ctxt_switches: Cambios de contexto voluntarios e involuntarios
  • Sig*: información sobre señales (que /proc/PID/stat no nos la daba muy bien.
  • Información sobre memoria tanto residente como virtual. Echad un vistazo al fichero para ver el montón de elementos que podemos consultar.

Otros ficheros de interés

Podremos consultar muchas más cosas del proceso. Basta con hacer ls /proc/PID aunque para ahorrar trabajo os dejo aquí algunos de los más interesantes (también tenemos que tener en cuenta que a medida que salen versiones nuevas del kernel Linux podremos ver más información):

  • /proc/PID/maps : Mapa de memoria. Ahí podemos ver rangos de memoria, tipo de acceso (lectura, escritura, ejecución y si es pública o privada) y si hay un fichero mapeado en esa dirección, o estamos en memoria de datos, o es pila, etc. Si estás estudiando Sistemas Operativos es recomendable echarle un ojo a uno de estos archivos, eso sí, empezad por una aplicación pequeña, porque un firefox, o un libreoffice puede ser muy heavy de analizar.
  • /proc/PID/oom_score : Esto tiene que ver con el Out Of Memory Killer. Mirad este post.
  • /proc/PID/mounts : Dispositivos o discos montados de cara a la aplicación.
  • /proc/PID/limits : Límite de memoria, procesos, ficheros abiertos, bloqueos, tiempo de CPU y más que tiene este proceso.
  • /proc/PID/exe : Éste es el ejecutable que se ha cargado. ¡Éste y no otro! Por si alguien nos intenta engañar ejecutando un ejecutable que no es, que está en otra ruta, es otra versión. Aquí tenemos el ejecutable que podremos leer con:
    readelf -a /proc/PID/exe

En definitiva, encontramos mucha información para analizar los procesos en ejecución. Y, si tienes un proyecto al respecto, déjamelo en los comentarios, que lo enlazaré encantado 🙂

Buscar todos los procesos

Por último, un pequeño código de ejemplo para buscar los PID de todos los procesos en ejecución, y poder analizarlos como hace ps, o top. O incluso para que nosotros hagamos alguna clase de análisis de procesos en tiempo real.
La clave está en listar archivos, tal y como lo haría ls. Sólo que dentro de proc y quedarnos con los que tienen aspecto numérico. En mi caso confío un poco en Linux y espero que si un fichero dentro de proc empieza por un número va a ser un PID. Obviamente en proc no suele haber cosas raras, aunque no podemos descartar que algún módulo del kernel pueda hacer de las suyas y tengamos que verificar que todos los caracteres son numéricos y que se trata de un directorio e incluso buscar la existencia de ciertos archivos dentro:

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 <errno.h>
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>

/* Error fatal! */
void panic(char* error);

int main(int argc, char* argv[])
{
    DIR* proc_dir;
    struct dirent *ent;
    unsigned total = 0;
    proc_dir = opendir("/proc");
    while ((ent = readdir(proc_dir)))
      {
        if ((*ent->d_name>'0') && (*ent->d_name<='9')) /* Be sure it's a pid */
          {
        printf("%s\n", ent->d_name);
        ++total;
          }
      }
    closedir(proc_dir);
  printf ("Total de procesos: %u\n", total);
}


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

Una biblioteca en C++ que tiene muchas cosas ya hechas

Después de toda esta guía, voy con el autobombo. Hace tiempo hice un pequeño archivo .h para C++ moderno que entre otras cosas, analiza procesos y guarda las cosas en estructuras muy apañadas para la ocasión. Encontramos el código en GitHub. Con un ejemplo listo para compilar que aclara muchas cosas.
Foto principal: Daryan Shamkhali

The post Obtener información básica sobre procesos del sistema Linux en C y C++ (parte 3) appeared first on Poesía Binaria.

» Leer más, comentarios, etc...

Blog Bitix

Ejecutar varias tareas de forma concurrente en Java

julio 23, 2017 11:00

Java

En el artículo de iniciación a la programación concurrente en Java explicaba las facilidades que proporciona este lenguaje para la programación de tareas con varios hilos que implica la sincronización y bloqueo mediante varias primitivas como semáforos o locks, ponía el ejemplo y su código de dos de los típicos ejemplos que suelen usarse en las asignaturas de sistemas operativos, el problema de los filósofos y el del barbero.

En el caso de tener varias tareas que tardan unos segundos si las ejecutamos de forma secuencial el tiempo que tardarán será la suma de todas las tareas. Si las tareas no son dependientes, no hace falta esperar a que termine una anterior para comenzar otra, o el problema se puede descomponer en varias partes ejecutándolas de forma concurrente y simultáneamente el tiempo total que tardarán aproximadamente será el tiempo de la tarea más lenta.

En la API de Java además de las primitivas de sincronización se ofrece además algunas clases para manejar hilos y tareas a ejecutar de forma concurrente sin tener que manejar los hilos a bajo nivel. La clase ExecutorService permite crear un pool de threads con un número fijo de ellos, el pool reutilizará cada thread para ir ejecutando las tareas. Crear threads es una operación más o menos costosa con lo que reutilizándolos se aprovecha mejor los recursos del sistema y en un número grande de tareas a ejecutar la mejora en el rendimiento quizá se note. Crear un pool con un número fijo y limitado de threads evita que el sistema se sature o por el contrario esté infrautilizado, configurando el tamaño del pool de threads según las características del sistema que las ejecutará y del tipo de recursos que más utiliza las tareas se obtiene el mejor rendimiento posible.

Con el método Runtime.availableProcessors se obtiene el número de núcleos lógicos del ordenador de los modernos procesadores que utilizan Hyper Threading tanto los de Intel como AMD, si las tareas hacen un uso muy intensivo de CPU y poco de entrada/salida el tamaño del pool de threads óptimo será cercano al número de núcleos del procesador. Por el contrario, si las tareas hacen un uso intensivo de de entrada/salida el tamaño del pool de threads óptimo será mayor ya que estarán la mayor parte del tiempo esperando a que finalicen las lentas operaciones de entrada y salida comparadas con la CPU.

Suponiendo que una aplicación ha de realizar varias consultas a una base de datos para presentar su información al usuario, esas consultas y por la cantidad de información que tiene la base de datos o porque los índices no ayudan tardan en ejecutarse 3 segundos, teniendo que realizar 8 de estas consultas el tiempo total que tardará la aplicación en presentar la información será de 24 segundos (8 tareas x 3 segundos/tarea) ejecutando las consultas de forma secuencial. 24 segundos es un tiempo considerable y el usuario pensará que la aplicación no responde. Ejecutando las tareas con un pool de 8 threads el tiempo total empleado para presentar la información será de 3 segundos y con un pool de 4 threads el tiempo será de 6 segundos, mucho menos que los 24 de forma secuencial.

Este es el código para ejecutar tareas de forma secuencial y de forma concurrente con un pool de threads de tamaño el doble del número de procesadores del sistema midiendo además el tiempo total para comprobar la diferencia de tiempos de ambas opciones.

<noscript><a href="https://asciinema.org/a/vuoXPba1ks9aOg5Ebm3MTkIbM" target="_blank"><img src="https://asciinema.org/a/vuoXPba1ks9aOg5Ebm3MTkIbM.png" width="734" /></a></noscript> Ejemplo de ejecución secuencial y concurrente

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

» Leer más, comentarios, etc...

Koalite

Seis añitos

julio 21, 2017 05:06

Sin hacer muchos alardes y un poco por los pelos, parece que he conseguido llegar al sexto año con el blog.

En total son ya 326 entradas (algunas tan inútiles como ésta) en las que he repasado temas de lo más variado, desde muy técnicos y específicos hasta ideas raras que me rondan por la cabeza, pasando por aspectos genéricos del desarrollo de software e historias de abuelo cebolleta.

Siempre he mantenido que escribo el blog principalmente para mi, pero debo reconocer que después de tanto tiempo hay momentos en los que me siendo en deuda con la gente que se ha molestado en dejar comentarios, enseñarme cosas y hacerme pasar muchos buenos ratos. Por eso, y aunque siempre he odiado este tipo de posts cuando me los encuentro en otros blogs, creo que los que compartís conmigo un rato de vez en cuando leyendo alguno de mis posts os mereceis saber qué va a ser de este blog.

No hace falta ser ningún lince para darse cuenta de que últimamente la frecuencia de posteo ha ido disminuyendo. Si hace unos años había un post todas las semanas, mi objetivo en los dos últimos años ha sido menos ambicioso, y en los últimos meses a duras penas consigo publicar un post cada dos semanas. Me temo que eso no va a cambiar en un futuro próximo.

Ahora mismo estoy embarcado en un proyecto personal de casi cinco meses y siete kilos de peso que tiene prioridad máxima para mi. La buena noticia es que, con mayor o menor actividad, mi intención es mantener el blog vivo y seguir escribiendo siempre que tenga un hueco. Además, mi referente me ha dicho que es normal pasar por rachas así y yo confío plenamente en él ;-)

Y a descansar unas semanas

Cada año llego a estas fechas con las energías más justas (será cosa de ser un viejo), y los últimos 10 meses han sido especialmente agotadores, así que toda disfrutar de las vacaciones, del nuevo proyecto, y descansar unas semanas.

Como siempre, descansar no significa olvidarme (por completo) del mundo del desarrollo, y seguiré quejándome de cosas y discutiendo por nimiedades en twitter.

¡Disfrutad del verano!

No hay posts relacionados.

» Leer más, comentarios, etc...

Picando Código

Firefox Focus: 1 millón de descargas y 3 nuevas características pedidas por usuarios

julio 20, 2017 08:00

Desde que Mozilla publicó Firefox Focus para Android (¡hace menos de un mes!), la aplicación fue descargada 1 millón de veces. Ya cuentan con más de 8.000 comentarios, y la aplicación tiene un rating de 4.5 estrellas. Muy buena respuesta por parte de la comunidad al hermoso navegador simple, rápido y orientado a la privacidad que nos brinda Mozilla.

Firefox Focus

En mi caso vengo usándolo prácticamente a diario, es al que voy por defecto. Si necesito algo extra, puedo abrir el sitio que estoy viendo en Firefox estándar. También he conseguido que más de una persona empiece a usarlo por puras recomendaciones (Mozilla, give me free stuff!).

Junto a este festejo se agregaron 3 nuevas características. Prestando atención a los comentarios de los usuarios, se juntaron las características que se pudieran agregar rápidamente y en menos de un mes del lanzamiento, ahora contamos con:

  • Videos a pantalla completa: Algo que estaba esperando, aunque justo no funciona (todavía) para YouTube… Parece que se volvió una prioridad debido a los comentarios de usuarios. “Entendemos que si vas a mirar un video en tu teléfono, sólo vale la pena si se puede expandir al tamaño completo de la pantalla de tu teléfono celular.” Se agregó soporte para la mayoría de los sitios siendo YouTube la excepción más notable. El soporte para YouTube depende de la corrección de un bug por parte de Google y será incluido apenas esté arreglado.
  • Soporte de descargas: Se actualizó la aplicación para permitir descarga de archivos de todo tipo
  • Actualización de las acciones de las notificaciones: Ahora no sólo sirven para recordarnos borrar el historial. También incluyen la opción de abrir Firefox Focus a través de las notificaciones.

Mozilla tiene la misión de asegurarse que sus productos satisfagan las necesidades de los usuarios.Responder al feedback con mejoras rápidas y notables es su forma de decir gracias y dejarnos saber que nos están escuchando. Puedes descargar la última versión de Firefox Focus en Google Play App Store.

» Leer más, comentarios, etc...

Bitácora de Javier Gutiérrez Chamorro (Guti)

FAST Compiler

julio 20, 2017 07:32

Durante mis comienzos, me topé gracias al Shareware, con una grata sorpresa. Se trataba del lenguaje de programación FAST. Un lenguaje que jamás llegué a conocer a nadie que lo conociera, pero que en aquellos días de Turbo BASIC, me parecía increíble. FAST Compiler, generaba archivos .COM para DOS extremadamente pequeños, y ofrecía una velocidad […]

La entrada FAST Compiler aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

» Leer más, comentarios, etc...

Blog Bitix

4 formas de hacer un bucle for en Java

julio 19, 2017 08:30

Java

Hasta Java 5 para hacer un bucle desde 0 a N elementos había que usar una variable para mantener un contador, hacer una comparación para comprobar si se había llegado al límite e incrementar la variable en la siguiente ejecución. El código era bastante verboso y dado que los bucles son una construcción básica de cualquier lenguaje de programación es empleada numerosas veces en cualquier algoritmo. Antes de Java 5 un bucle for de 0 a 5 y de una colección se realizaba de la siguiente manera:

En Java 5 el bucle for se enriqueció notablemente, con el foreach se puede recorrer una colección y cualquier objeto que implemente la interfaz Iterable. Con el bucle foreach una Collection se recorre de la siguiente manera.

Pero esto es para las colecciones si se quiere hacer un bucle un número fijo de veces como en el primer caso de 0 a 5 conociendo que para usar el foreach basta que le indiquemos un objeto que implemente la interfaz Iterable podemos usar la siguiente expresión y su implementación que tiene la ventaja de no tener que incluir la expresión de comparación y el incremento de la variable, la clase Counter implementa la interfaz Iterable y devuelve un Iterator sobre los valores del rango indicado:

En Java 8 con la introducción de los Stream y de IntStream podemos usar el método range y rangeClosed para obtener un Stream de enteros y hacer un bucle con un comportamiento similar a los anteriores.

Los Stream de Java 8 están muy bien para simplificar algunas operaciones complejas pero para un bucle for sencillo tiene sus inconvenientes como ofuscar significativamente el stacktrace en caso de producirse alguna excepción. Se puede usar cualquier opción pero la primera con el tradicional bucle for sea la menos recomendable teniendo a nuestra disposición la clase Counter con Java 5 o los Stream y lambdas con Java 8.

El siguiente programa muestra las cuatro opciones, su salida en la consola sería el siguiente:

4 formas de hacer un bucle en Java

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

» Leer más, comentarios, etc...

Variable not found

await EnjoyVacationAsync()

julio 18, 2017 06:55

Pues sí, por fin llegó el verano, esa época en la que durante unos días podemos cambiar nuestra silla de oficina por una hamaca, y nuestro pequeño monitor por un bonito atardecer en la playa en formato panorámico :)

Por mi parte, intentaré hacer una parada completa de un par de semanas comenzando el próximo lunes, y después volveré al tajo, aunque intentado que sea de forma menos intensa que de costumbre. En lo relativo al blog, supongo que volveré a tomar los mandos ya entrado septiembre; hasta ese momento lo dejo en vuestras manos, así que ¡cuidádmelo! ;)

Costa Ballena, Rota (Cádiz)
Imagen: Costaballena - Minube.com

Espero que cuando os llegue el turno paséis unas felices vacaciones y descanséis, siempre en buena compañía. Aprovechad para formatearos mentalmente y recargar pilas para la temporada 2017-18 que, como siempre, promete ser apasionante.

Pero eso sí, recordad que a la vuelta estaremos por aquí de nuevo, buscando la variable :)

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 291

julio 17, 2017 06:55

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

.NET/.NET Core

ASP.NET/ASP.NET Core

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Otros


Por último, permitidme un consejo por el bien de la humanidad: poned mucho cuidado cuando programáis, pues un error tonto podría tener consecuencias catastróficas. ¿Quién no ha introducido alguna vez una asignación en lugar de una comparación de igualdad?

Cuidado al confundir "=" con "=="

Publicado en Variable not found

» Leer más, comentarios, etc...

Adrianistán

Formulario de registro en Elm

julio 15, 2017 02:41

Recientemente he estado experimentando con Elm, un lenguaje funcional puro y a la vez framework de backend. De momento he programado ya he visto unas cuántas cosas y puedo decir que me gusta bastante, que es un soplo de aire fresco en el mundo web y que sinceramente no creo que vaya a triunfar algún día. Sin embargo para proyectos personales, lo veo como alternativa superior a Angular, React y Vue.

Uno de los ejercicios que puede venir bien para entender Elm es implementar un formulario de registro con normas para la contraseña. Según vayamos escribiendo se nos informa sobre qué errores tiene la contraseña para ser segura y cuando lo sea mostramos el botón de registro.

Es un código un poco tonto pero me apetecía compartirlo.

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
import Char
import String
import List

main : Program Never Model Msg
main = Html.program {init = init, view = view, update = update, subscriptions = subscriptions}

type alias Model =
{
    name : String,
    password : String,
    passwordAgain : String
}

init : (Model, Cmd Msg)
init = (Model "" "" "", Cmd.none)

subscriptions : Model -> Sub Msg
subscriptions model = Sub.none

type Msg = Name String | Password String | PasswordAgain String

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        Name name -> ({ model | name = name},Cmd.none)
        Password pass -> ({model | password = pass},Cmd.none)
        PasswordAgain pass -> ({model | passwordAgain = pass},Cmd.none)

view : Model -> Html Msg
view model =
    div []
        [
            input[type_ "text", placeholder "Name", onInput Name][],
            input[type_ "password", placeholder "Password", onInput Password][],
            input[type_ "password", placeholder "Confirm", onInput PasswordAgain][],
            viewValidate model
        ]

viewValidate : Model -> Html Msg
viewValidate model =
    let list = checkMinLength model :: checkPassword model :: checkUpper model :: checkLower model :: checkDigit model :: []
    wrongList = List.filter (\(x,y) -> not x) list
    in
    if List.length wrongList == 0 then
        div [] [button[]]
    else
        div [] (List.map showError wrongList )

showError : (Bool,String) -> Html Msg
showError (_,error) =
    div [style[("color","red")]] 

checkPassword : Model -> (Bool, String)
checkPassword model =
    if model.password == model.passwordAgain then
        (True,"")
    else
        (False,"Passwords do not match")

checkMinLength : Model -> (Bool,String)
checkMinLength model =
    if String.length model.password > 7 then
        (True,"OK")
    else
        (False,"Password must have a minimum length of 8 characters")

checkUpper : Model -> (Bool,String)
checkUpper model =
    let isUp = String.any Char.isUpper model.password
    in
    if isUp then
        (True,"")
    else
        (False,"The password must contain an upper letter")

checkLower : Model -> (Bool,String)
checkLower model =
    let isDown = String.any Char.isLower model.password
    in
    if isDown then
        (True,"")
    else
        (False,"The password must contain a lower letter")

checkDigit : Model -> (Bool,String)
checkDigit model =
    let isDig = String.any Char.isDigit model.password
    in
    if isDig then
        (True,"")
    else
        (False,"The password must contain a digit")

 

La entrada Formulario de registro en Elm aparece primero en Blog - Adrianistan.eu.

» Leer más, comentarios, etc...

Blog Bitix

Tutorial sobre programación concurrente en Java

julio 14, 2017 10:00

Java proporciona en su API numerosas primitivas para realizar programación concurrente. La programación concurrente permite realizar varias tareas simultáneamente aprovechando los múltiples núcleos de los procesadores modernos con un tiempo de ejecución total para un conjunto de tareas significativamente menor. Dos de los problemas de concurrencia más conocidos son el problema de los filósofos y del barbero que en este artículo muestro como implementar usando varias de las primitivas ofrecidas por Java.

Java

En todo el tiempo que llevo programando en Java no he tenido necesidad de conocer en detalle las primitivas de concurrencia que ofrece el lenguaje y la API. Java desde sus primeras versiones ya ofrecía el soporte básico para la programación concurrente con las clases Thread y Runnable y algunas primitivas de sincronización como la palabra clave reservada syncrhonized, los locks intrínsecos de los objetos y algunos métodos de la clase Thread como sleep, wait y join. Entre la documentación de Java está el siguiente tutorial sobre la concurrencia en Java que es muy recomendable leer.

Las computadoras realizan varias tareas de forma concurrente con la ayuda del sistema operativo que permite compartir el procesador para realizar diferentes tareas (navegar por internet, editar un documento, escuchar música, …) cambiando cada muy poco tiempo (medido en ms) entre procesos, con los procesadores de varios núcleos las tareas se ejecutan silmultáneamente en diferentes núcleos. Los threads en Java se comunican principalmente compartiendo referencias a objetos, este tipo de comunicación es eficiente pero posibilita dos tipos de errores, interferencias entre threads y errores de consistencia, la herramienta para evitarlos es la sincronización. Sin embargo, la sincronización introduce contención cuando dos o más hilos intentan acceder al mismo recurso simultáneamente y provocan una pérdida de rendimiento. El bloqueo mutuo o deadlock, la inanición o starvation y un bloqueo vivo o livelock son problemas de la sincronización. Seguramente te suenen los objetos inmutables, en la programación concurrente son especialmente útiles dado que su estado no cambia y no pueden corromperse ni quedar en un estado inconsistente por la interferencia entre threads evitando de esta manera errores que suelen ser difíciles de depurar por ofrecer un comportamiento errático.

En vez de usar los locks implícitos de los objetos la API de Java para concurrencia ofrece varios tipos más con propiedades adicionales como la habilidad de salir si el intento de adquirir el lock falla. En el paquete java.util.concurrent.locks está listados. Otro tipo de primitivas de sincronización para threads son los Semaphore, CyclicBarrier y CountDownLatch entre otros como Phaser y Exchanger. En el paquete java.util.concurrent.atomic hay varios tipos de datos básicos que realizan sus operaciones de forma atómica como por ejemplo contadores.

Con los Executors y ExecutorService no hace falta que manejemos los hilos a bajo nivel, es posible obtener un pool de threads de una tamaño específico y enviar clases Callable o Runnable que devuelven un resultado para que se ejecuten con un thread del pool cuando esté libre. Con la clase ScheduledExecutorService se programa la ejecución de tareas de forma periódica. En los streams añadidos a Java 8 el procesamiento se puede realizar de forma paralela aprovechando los microprocesadores multinúcleo sin tener que usar de forma explícita ninguna de las utilidades anteriores, internamente usa el Fork/Join.

El soporte para la programación concurrente ofrecido en Java es suficiente para la mayoría de tareas que podamos necesitar y ha mejorado bastante desde las primeras versiones.

El primer ejemplo que muestro es usando concurrencia ejecutar varias tareas y como realizándolas de forma secuencial el tiempo total empleado es la suma del tiempo de las tareas individuales y como usando concurrencia es la suma de la tarea que más tarda. El ejemplo se trata de 8 tareas que de forma secuencial tardan aproximadamente 24 segundos ya que cada tarea emplea 3 segundos, en el caso empleando concurrencia el tiempo es de aproximadamente 6 segundos ya se se emplea en pool de threads de 4 de capacidad con lo que las primeras 4 tareas tardan 3 segundos y el siguiente lote de 4 tareas tarda otros 3 segundos para un total de 6 segundos.

<noscript><a href="https://asciinema.org/a/GjXM3ACFPPW027eLqKiZlrvMh" target="_blank"><img src="https://asciinema.org/a/GjXM3ACFPPW027eLqKiZlrvMh.png" width="734" /></a></noscript> Ejecución secuencial y concurrente de tareas

Dos de los problemas más conocidos en la programación concurrente son el de La cena de los filósofos y el de El barbero durmiente. Usando algunas de las primitivas comentadas en este artículo este sería el código para para resolver ambos problemas en Java.

En este código del problema de los filósofos la clase Table crea los filósofos asignándoles los Fork que tienen que compartir para comer después de estar un tiempo pensando. En la ejecución se observa que el primer filósofo que intenta comer puede hacerlo ya que sus tenedores adyacentes está libres pero posteriormente se observa que en algunas ocasiones algún filósofo no puede hacerlo porque sus tenedores están siendo usados por alguno de sus compañeros adyacentes.

<noscript><a href="https://asciinema.org/a/kxDfAc6SNNZ3EFdR4Zrc1U163" target="_blank"><img src="https://asciinema.org/a/kxDfAc6SNNZ3EFdR4Zrc1U163.png" width="734" /></a></noscript> Ejemplo de concurrencia de los filósofos

En el caso de ejemplo del barbero cuando solo hay un barbero los clientes se acumulan ya que estos entran en la tienda a razón de 1 entre 1500 y 3500ms y el barbero tarda afeitar un cliente entre 2000 y 7000ms. Poniendo en la barbería dos barberos los clientes ya no se acumulan en la sala de espera.

<noscript><a href="https://asciinema.org/a/uej4sdDRO03kAcROgu4RAc27z" target="_blank"><img src="https://asciinema.org/a/uej4sdDRO03kAcROgu4RAc27z.png" width="734" /></a></noscript> Ejemplo de concurrencia del barbero (1 barbero)
<noscript><a href="https://asciinema.org/a/etiWqKLzR4BI3HpOLawuzSwuo" target="_blank"><img src="https://asciinema.org/a/etiWqKLzR4BI3HpOLawuzSwuo.png" width="734" /></a></noscript> Ejemplo de concurrencia del barbero (2 barberos)

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

» Leer más, comentarios, etc...

Blog Bitix

Iniciación a la programación concurrente en Java

julio 14, 2017 10:00

Java proporciona en su API numerosas primitivas para realizar programación concurrente. La programación concurrente permite realizar varias tareas simultáneamente aprovechando los múltiples núcleos de los procesadores modernos con un tiempo de ejecución total para un conjunto de tareas significativamente menor. Dos de los problemas de concurrencia más conocidos son el problema de los filósofos y del barbero que en este artículo muestro como implementar usando varias de las primitivas ofrecidas por Java.

Java

En todo el tiempo que llevo programando en Java no he tenido necesidad de conocer en detalle las primitivas de concurrencia que ofrece el lenguaje y la API. Java desde sus primeras versiones ya ofrecía el soporte básico para la programación concurrente con las clases Thread y Runnable y algunas primitivas de sincronización como la palabra clave reservada syncrhonized, los locks intrínsecos de los objetos y algunos métodos de la clase Thread como sleep, wait y join. Entre la documentación de Java está el siguiente tutorial sobre la concurrencia en Java que es muy recomendable leer.

Las computadoras realizan varias tareas de forma concurrente con la ayuda del sistema operativo que permite compartir el procesador para realizar diferentes tareas (navegar por internet, editar un documento, escuchar música, …) cambiando cada muy poco tiempo (medido en ms) entre procesos, con los procesadores de varios núcleos las tareas se ejecutan silmultáneamente en diferentes núcleos. Los threads en Java se comunican principalmente compartiendo referencias a objetos, este tipo de comunicación es eficiente pero posibilita dos tipos de errores, interferencias entre threads y errores de consistencia, la herramienta para evitarlos es la sincronización. Sin embargo, la sincronización introduce contención cuando dos o más hilos intentan acceder al mismo recurso simultáneamente y provocan una pérdida de rendimiento. El bloqueo mutuo o deadlock, la inanición o starvation y un bloqueo vivo o livelock son problemas de la sincronización. Seguramente te suenen los objetos inmutables, en la programación concurrente son especialmente útiles dado que su estado no cambia y no pueden corromperse ni quedar en un estado inconsistente por la interferencia entre threads evitando de esta manera errores que suelen ser difíciles de depurar por ofrecer un comportamiento errático.

En vez de usar los locks implícitos de los objetos la API de Java para concurrencia ofrece varios tipos más con propiedades adicionales como la habilidad de salir si el intento de adquirir el lock falla. En el paquete java.util.concurrent.locks está listados. Otro tipo de primitivas de sincronización para threads son los Semaphore, CyclicBarrier y CountDownLatch entre otros como Phaser y Exchanger. En el paquete java.util.concurrent.atomic hay varios tipos de datos básicos que realizan sus operaciones de forma atómica como por ejemplo contadores.

Con los Executors y ExecutorService no hace falta que manejemos los hilos a bajo nivel, es posible obtener un pool de threads de una tamaño específico y enviar clases Callable o Runnable que devuelven un resultado para que se ejecuten con un thread del pool cuando esté libre. Con la clase ScheduledExecutorService se programa la ejecución de tareas de forma periódica. En los streams añadidos a Java 8 el procesamiento se puede realizar de forma paralela aprovechando los microprocesadores multinúcleo sin tener que usar de forma explícita ninguna de las utilidades anteriores, internamente usa el Fork/Join.

El soporte para la programación concurrente ofrecido en Java es suficiente para la mayoría de tareas que podamos necesitar y ha mejorado bastante desde las primeras versiones.

El primer ejemplo que muestro es usando concurrencia ejecutar varias tareas y como realizándolas de forma secuencial el tiempo total empleado es la suma del tiempo de las tareas individuales y como usando concurrencia es la suma de la tarea que más tarda. El ejemplo se trata de 8 tareas que de forma secuencial tardan aproximadamente 24 segundos ya que cada tarea emplea 3 segundos, en el caso empleando concurrencia el tiempo es de aproximadamente 6 segundos ya se se emplea en pool de threads de 4 de capacidad con lo que las primeras 4 tareas tardan 3 segundos y el siguiente lote de 4 tareas tarda otros 3 segundos para un total de 6 segundos.

<noscript><a href="https://asciinema.org/a/GjXM3ACFPPW027eLqKiZlrvMh" target="_blank"><img src="https://asciinema.org/a/GjXM3ACFPPW027eLqKiZlrvMh.png" width="734" /></a></noscript> Ejecución secuencial y concurrente de tareas

Dos de los problemas más conocidos en la programación concurrente son el de La cena de los filósofos y el de El barbero durmiente. Usando algunas de las primitivas comentadas en este artículo este sería el código para para resolver ambos problemas en Java.

En este código del problema de los filósofos la clase Table crea los filósofos asignándoles los Fork que tienen que compartir para comer después de estar un tiempo pensando. En la ejecución se observa que el primer filósofo que intenta comer puede hacerlo ya que sus tenedores adyacentes está libres pero posteriormente se observa que en algunas ocasiones algún filósofo no puede hacerlo porque sus tenedores están siendo usados por alguno de sus compañeros adyacentes.

<noscript><a href="https://asciinema.org/a/kxDfAc6SNNZ3EFdR4Zrc1U163" target="_blank"><img src="https://asciinema.org/a/kxDfAc6SNNZ3EFdR4Zrc1U163.png" width="734" /></a></noscript> Ejemplo de concurrencia de los filósofos

En el caso de ejemplo del barbero cuando solo hay un barbero los clientes se acumulan ya que estos entran en la tienda a razón de 1 entre 1500 y 3500ms y el barbero tarda afeitar un cliente entre 2000 y 7000ms. Poniendo en la barbería dos barberos los clientes ya no se acumulan en la sala de espera.

<noscript><a href="https://asciinema.org/a/BeA6bcKy5yoSGfByRUfE1HfYD" target="_blank"><img src="https://asciinema.org/a/BeA6bcKy5yoSGfByRUfE1HfYD.png" width="734" /></a></noscript> Ejemplo de concurrencia del barbero (1 barbero)
<noscript><a href="https://asciinema.org/a/K7Ug6RT60mjWRbNcwRGapw7V6" target="_blank"><img src="https://asciinema.org/a/K7Ug6RT60mjWRbNcwRGapw7V6.png" width="734" /></a></noscript> Ejemplo de concurrencia del barbero (2 barberos)

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

» Leer más, comentarios, etc...

xailer.info

Lanzamiento de Xailer 5

julio 14, 2017 12:27

Estimados usuarios de Xailer,

La nueva versión 5 de Xailer ya está disponible. Más información en los siguientes enlaces:

https://www.xailer.com/?noticias
http://www.xailer.com/download/?es&file=1

Un cordial saludo

» Leer más, comentarios, etc...

Bitácora de Javier Gutiérrez Chamorro (Guti)

Beep/Bell en ensamblador

julio 14, 2017 07:04

He tenido algo de tiempo para entretenerme programando, una de esas cosas, que resultan prácticamente inútiles, pero con las que puedes llegar a disfrutar. Estaba revisando nuevamente JWASM como hice hace algunos años en FPS en ensamblador, y se me ocurrió rememorar viejos conocimientos con este programilla. Se trata de BEEP, un programa, en realidad […]

La entrada Beep/Bell en ensamblador aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

» Leer más, comentarios, etc...

Poesía Binaria

Permitir que nuestros usuarios ejecuten tareas controladas en nombre de otro. #SUID #SGID – Con ejemplos y ataques

julio 12, 2017 08:12

Uno de los puntos fuertes de un sistema Unix es su forma de gestionar los privilegios de usuario. Se suele decir que el malware en GNU/Linux sólo afecta al usuario por el que entra (y podrá hacer todo lo que éste pueda hacer), por lo tanto será muy difícil (nada es imposible, y siempre hay bugs) que un programa ejecutado con privilegios de usuario normal afecte al sistema completo.

Eso sí, otro de los puntos fuertes de un sistema tipo Unix, es que nos deja automatizar absolutamente todo. Podemos crear sistemas que trabajen solos, que atiendan a eventos y éstos disparen acciones dentro del sistema, casi sin límite. Como muchas tareas dependen de datos externos, se ejecutan multitud de programas (cada uno de su padre y de su madre), si queremos mantener un sistema seguro, debemos hacer que el usuario que haga esa automatización de tareas sea un usuario con los menores privilegios posibles. Aunque puede que alguna vez necesite ejecutar algo con mayores privilegios, incluso privilegios root.

Una breve introducción (permisos sobre archivos)

En este tipo de sistemas, cuando consultamos los permisos de acceso de un archivo (por ejemplo con ls), veremos algo como esto:

ls -latr test
-rwxr-xr-- 1 gaspy oficina 8608 mar 11 20:40 test

Veremos (de derecha a izquierda) el nombre, la fecha, el tamaño, el grupo, el usuario, número de enlaces y privilegios. Lo que nos interesa son los privilegios. Son 10 caracteres, primero encontramos un carácter que indicará si es un archivo, directorio, enlace, pipe, socket o cualquier otra cosa, y luego tres grupos de tres que pueden ser r (readable), w (writable) y x (executable); es decir, cada grupo puede tener activa la lectura, escritura y la ejecución de forma individual dependiendo de lo que puede hacer con el archivo.
El primer grupo, que en este caso es rwx, corresponde al dueño del fichero, el usuario (u) que aparece al lado, es decir, gaspy. En este caso, el usuario gaspy podrá leer, escribir y ejecutar el fichero.
El segundo grupo, que es r-x (como vemos, no tiene la w), corresponde a los permisos que tendrá cualquier usuario que pertenezca al grupo (g), es decir, cualquier usuario del grupo oficina sólo podrá leer y ejecutar el archivo.
El tercer grupo, r– (no tiene w, ni x), corresponde a los permisos que tendrá cualquier otro usuario del sistema (o), vamos un usuario que no sea el dueño, ni pertenezca al grupo oficina sólo podrá leer el archivo.

Bueno, no digo nada de root porque éste lo puede hacer todo, ya lo dice el dicho: “Cada user en su home y root en la de todos”.

Eso sí, como vemos, cada grupo puede tener cualquier combinación de rwx para un mismo archivo, cada elemento (rwx) corresponde con un bit, lo que hacen que los permisos se representen con 3bits. Además cada grupo puede tener ocho combinaciones diferentes de rwx (—, –x, -w-, -wx, r–, r-x, rw-, rwx) por lo que podemos representarlas por un número entre el 0 y el 7. Por lo tanto, muchas veces podremos representar los permisos con su forma numérica, incluso con 3 números, podríamos representar los permisos de los tres grupos. Por ejemplo, podría ser 754 (ningún número podrá ser mayor a 7).

Para cambiar los permisos de un archivo, podemos hacerlo con chmod:

chmod o+r mi_archivo

Con esta línea, daremos permiso de lectura a cualquier usuario sobre el archivo mi_archivo. También podremos hacer:

chmod 777 test

Ahora estaremos dando permiso de lectura/escritura/ejecución sobre el archivo test a todo el mundo.

SETUID y SETGID

Estos bits indican que vamos a ejecutar dicho archivo como si fuéramos el usuario y/o el grupo dueños del archivo. Por lo tanto podremos hacer cosas que tal vez el usuario normal no puede hacer. Y es común utilizar estos bits para hacer que un usuario ejecute tareas como root o como cualquier otro usuario que especifiquemos. Eso sí, debemos definir bien los permisos.
Vamos a empezar con un ejemplo sencillo, primero vamos a crear un programa en C que ponga en pantalla el usuario actual, en realidad debería indicar el usuario efectivo, este usuario efectivo será el que verifique el sistema operativo para casi todas las tareas. Normalmente coinciden, pero en este tipo de casos se separan los caminos de cada uno y aunque un programa realmente lo lanza un usuario sin privilegios, el usuario efectivo debería ser root.
El programa en C será el siguiente (muy sencillo, sin comprobación de errores y lo llamaremos quiensoy.c):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    struct passwd *pww;

    pww = getpwuid(geteuid());
    printf ("USER: %s\n",  pww->pw_name);

    return 0;
}

Ahora compilamos, y ejecutamos:

gcc -o quiensoy quiensoy.c
./quiensoy
USER: gaspy

Luego le damos privilegios (con el usuario www-data, por ejemplo. Podría ser root si queremos):

sudo chown www-data:www-data quiensoy
sudo chmod u+s quiensoy
./quiensoy
USER: www-data

Dejo aquí un ejemplo con el que podemos ver también el grupo, usuario real y demás.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <sys/types.h>
#include <unistd.h>

int main()
{
    struct passwd *pww;
    struct group *grp;

    pww = getpwuid(geteuid());
    printf ("USER EFECTIVO: %s\n",  pww->pw_name);

    pww = getpwuid(getuid());
    printf ("USER REAL: %s\n",  pww->pw_name);

    grp = getgrgid(getgid());
    printf ("GRUPO REAL: %s\n",  grp->gr_name);

    grp = getgrgid(getegid());
    printf ("GRUPO EFECTIVO: %s\n",  grp->gr_name);

    return 0;
}

Ahora si compilamos, establecemos permisos y damos SGID y SUID veremos lo que pasa:

gcc -o quiensoy quiensoy.c
sudo chown www-data:openvpn quiensoy
sudo chmod u+s quiensoy
sudo chmod g+s quiensoy
./quiensoy
USER EFECTIVO: www-data
USER REAL: gaspy
GRUPO REAL: gaspy
GRUPO EFECTIVO: openvpn

De este modo podremos ejecutar acciones como si fuéramos otro usuario. Aunque tenemos que tener cuidado con lo que dejamos hacer a la gente.

SUID y SGID en scripts

Esto supone una gran brecha de seguridad. Si, por ejemplo el usuario pudiera escribir el script o crear un enlace a un fichero que llame el script, o sustituir algún programa (por ejemplo, un programa que use which para buscar un ejecutable y creemos un equivalente en una ruta a la que sí tengamos acceso… o bueno, se nos pueden ocurrir mil cosas por las cuales esto no sería seguro. Así que muchos sistemas operativos tipo Unix no te van a dejar… bueno sí te van a dejar, pero harán caso omiso del SUID/SGID que le hayamos dado, incluyendo las últimas versiones del kernel Linux. Aunque existe un ataque típico que consiste en aprovecharse del tiempo que tarda el kernel en leer en #! de comienzo del script así como el intérprete y cargar todo en memoria para hacer el cambiazo del script.

De todas formas, si queremos comprometer un sistema es un buen punto para probar, porque podemos alcanzar privilegios de forma muy fácil.

Hasta hace relativamente poco, podíamos utilizar scripts en perl con SUID, pero las últimas versiones ya no lo permiten. Podemos pensar que App Packer para perl nos haría el trabajo sucio, ya que crea un ejecutable directamente con nuestro script. Pero, tras unas comprobaciones de seguridad que hacen los ejecutables no es posible hacerlo. Existe una versión parcheada, pero si no es para experimentar yo no la usaría.

Otra cosa que podemos hacer es utilizar scripts en Python y compilarlos con cython. Si no queremos hacer programas en C, que a veces es la solución más pesada y con un script podemos hacer las cosas mucho más rápido. Los ejecutables quedarán algo grandes, pero es lo que tiene utilizar otro lenguaje y luego traducirlo. Podemos hacer algo como esto:

1
2
3
4
5
6
#!/usr/bin/python3

import os
import pwd

print ("QUIÉN SOY? {}".format(pwd.getpwuid(os.geteuid()).pw_name))

Ahora compilamos, asignamos permisos y demás:

cython --embed -3 quiensoy.py
gcc -I/usr/include/python3.5m quiensoy.c -o quiensoy -lpython3.5m
sudo chown root:root quiensoy
sudo chmod u+s quiensoy
./quiensoy
QUIÉN SOY? root

Envolventes de scripts

No lo recomiendo, para nada. Pero alguna vez, y sobre todo para hacer experimentos, está muy bien. El objetivo es crear un programa en C que ejecute un script y lo haga con privilegios del usuario elegido. Podemos hacerlo de múltiples maneras, ya que es un programa muy pequeño que sólo se encarga de llamar a otro programa:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <unistd.h>
#include <errno.h>
#include <stdio.h>

int main( int argc, char ** argv )
{
              if( setgid(getegid()) ) perror( "setgid" );
              if( setuid(geteuid()) ) perror( "setuid" );

              execl("./miscript", "./miscript", (char *) 0);

              return errno;
}

Pero vamos, no es nada recomendable, un usuario podría llegar a cambiar el contenido de miscript. Por ejemplo, en este programa (vulnerable por definición) podría cambiar la ruta desde la que se ejecuta el programa y así ./miscript seguramente no sea quien creemos que es. O simplemente podemos atacar una llamada de comando falseando la variable $PATH o cualquier otra variable de entorno.

De todas formas utilizar una envoltura de este tipo es prácticamente igual a utilizar sudo, que debidamente configurado y actualizado podría ser incluso más seguro.

Seguridad

¡ Ataques ! ¡ Ataques !
Es cierto que ningún sistema es 100% libre de fallos, y que cuanto menos código o programas ejecutemos con privilegios, mejor para nosotros y nuestra seguridad (menos puntos de rotura del sistema). De todas formas hay varios ataques clásicos y típicos para los programas ejecutados con SETUID/SETGID. En casi todos los sistemas están arreglados pero, tal vez nos encontremos ante un Unix pequeño (sobre todo de sistemas empotrados) o incluso en nuestra facultad tengan un sistema Unix de hace unos cuantos años y tengamos la necesidad de romperlo.

El primero de los ataques es un ataque IFS. Si has curioseado shell scripts, habrás visto que IFS es el Internal Field Separator, es decir, el separador de campos, o palabras, o comandos, etc. Entonces podríamos crear un archivo llamado “bin”, hacerlo ejecutable y poner lo que queramos ejecutar con privilegios. Al mismo tiempo, asignaremos a IFS el valor ‘/‘ (la barra de separación de directorios), para que cuando una envoltura ejecute ./miscript y el script intente procesar el intérprete ejecute en realidad nuestro fichero bin. Afortunadamente no suele funcionar desde hace muchos años, pero claro, siempre hay implementaciones pequeñas para IOT donde puede que funcione.

Otro ataque importante es el de la precarga de bibliotecas. Normalmente como usuarios podemos sustituir las bibliotecas que se cargan cuando ejecutamos un programa. Esto es muy útil para solucionar problemas en ciertos programas (sobre todo si no disponemos del código fuente), mantener retrocompatibilidad con bibliotecas y algunas cosas más. Aunque si lo llevamos al terreno de las maldades, podríamos sustituir todas las llamadas a printf() por lo que nosotros queramos, como abrir un intérprete de comandos, por lo que nada más ejecutar el primer printf() el programa abrirá un bash/sh/dash/etc. Eso sí, si el programa tenía activado el SUID o SGID, la shell la abrirá con el usuario dueño del archivo, lo cual puede ser muy malo. Afortunadamente, en la mayoría de sistemas esto ya no es un problema.

Otro posible ataque es debido a bugs de los programas instalados. Por ejemplo, hace poco tiempo, la aplicación nmap, muy útil para auditoría de red tenía el bit SUID definido en algunas distribuciones Linux, con lo que podíamos acceder a un modo interactivo y ejecutar un intérprete de comandos, con lo que teníamos root en muy poco tiempo. ¿Necesita nmap ese SUID? Si estamos en un servidor, ¿necesitamos nmap?
De todas formas, es una buena práctica de seguridad tener un listado de todos los archivos con SUID o SGID. De la siguiente forma:

find / -user root -perm -u=s -type f
/usr/sbin/pppd
/usr/bin/pkexec
/usr/bin/nvidia-modprobe
/usr/bin/passwd
/usr/bin/sudo
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/newgrp

Y de la misma forma que ponemos -u=s para SUID, probaremos -g=s para SGID. Lo primero es preguntarnos, ¿necesitamos todo esto?. Si es nuestro trabajo estar al tanto de la seguridad del sistema, deberíamos revisar uno a uno todos los archivos que se pueden ejecutar, y no estaría de más crear un informe sobre cada uno de ellos, para revisarlo en el futuro.
Además, es importante hacer esto de forma periódica para ver que no hay cambios, o asegurarnos de que los cambios están controlados.

OverlayFS exploit. Podemos probarlo, afectaba desde Linux 3.13 hasta Linux 3.19. Podemos encontrarlo aquí. Sólo hay que compilarlo y veremos si el programa nos permite escalar privilegios

SUID/SGID en nuestro día a día

Seguramente hayas utilizado esta característica en el pasado casi sin darte cuenta. Ya que necesitaríamos privilegios especiales para escribir cualquier archivo sobre el que no tengamos permiso, por ejemplo como hace la orden passwd. Este comando, que sirva para cambiar nuestra contraseña de usuario deberá escribir sobre un archivo que ni tan solo podemos leer por cuestiones de seguridad, /etc/shadow y para hacerlo, necesitamos obligatoriamente permisos de superusuario. Si hacemos stat podremos ver:

stat /usr/bin/passwd
Fichero: ‘/usr/bin/passwd’
Tamaño: 54256      Bloques: 112        Bloque E/S: 4096   fichero regular
Dispositivo: 813h/2067d Nodo-i: 203318      Enlaces: 1
Acceso: (4755/-rwsr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Acceso: 2017-06-27 22:23:51.194148876 +0200
Modificación: 2017-05-17 01:37:35.000000000 +0200
Cambio: 2017-05-22 02:50:43.470092977 +0200
Creación: --

Por supuesto, sudo. Cómo si no se las apaña para cambiar el usuario antes de hacer la ejecución de un programa. Aunque tenemos que cumplir con la propia seguridad de sudo (archivo sudoers).

Ping… ¡ehh! ¿Por qué ping requiere privilegios? ping lo utilizamos muchas veces para probar la red, saber si tenemos conexión y saber el tiempo de respuesta de un servidor. Pero ping necesita acceso directo a los protocolos de red y eso sólo lo puede hacer root.

Otros comandos podrían ser mount / umount, mlocate y algunos más. Nosotros incluso podríamos crear algunos programas para la administración de nuestros servidores, toma de decisiones, y mucho más. Eso sí, con mucho cuidado, y estudiando las posibilidades de un usuario o malware a afectar nuestra máquina. A veces, ésta no siempre es la mejor solución.

Foto principal: Alex Knight

The post Permitir que nuestros usuarios ejecuten tareas controladas en nombre de otro. #SUID #SGID – Con ejemplos y ataques appeared first on Poesía Binaria.

» Leer más, comentarios, etc...

Variable not found

Retorno de referencias y referencias locales en C#

julio 11, 2017 06:55

C#En C# siempre hemos podido enviar a métodos parámetros por referencia usando la palabra clave ref. Aunque su alcance era algo limitado, nos permitía coquetear con punteros para cubrir de forma eficiente algunos escenarios y permitirnos algunos usos avanzados, pero sin necesidad de abandonar la seguridad que nos ofrece el entorno de ejecución de .NET.

Un ejemplo clásico es el uso de ref para intercambiar dos valores desde un método:
int one = 1, two = 2;
Swap(ref one, ref two);
Console.WriteLine($"{one},{two}"); // 2,1
...

void Swap<T>(ref T a, ref T b)
{
var temp = a;
a = b;
b = temp;
}
En C#7, el ámbito de uso de las referencias se ha ampliado bastante gracias a la introducción de dos nuevas características en el lenguaje:
  • El soporte para variables locales de tipo referencia, o ref locals.
  • La capacidad de un método o función de retornar referencias, también conocida como ref returns.
Ninguna de estas características son ideas nuevas. Ya Eric Lippert tanteó sobre la posibilidad de implementarlas en el lenguaje hace más de siete años, pero no fue hasta hace dos años cuando volvió a saltar a la palestra y, tras la propuesta oficial, convertirse finalmente en una realidad.

Veamos en qué consisten.

1. Variables locales de tipo referencia (ref locals)

En C#7 podemos utilizar la palabra clave ref para definir variables de tipo referencia, es decir, crear punteros hacia otras variables o miembros que sean del mismo tipo estén visibles en el momento de la definición. Veamos un código de dudosa utilidad, pero que ilustra su sintaxis y forma de utilización en este contexto:
int original = 0;
ref int alias = ref original; // O bien, ref var alias = ref original;

alias = 18; // Sobreescribimos el contenido del destino de la referencia
Console.WriteLine(original); // 18;
Observad que estamos creando una variable local llamada alias que es una referencia hacia otra variable de tipo int definida en el mismo ámbito, aspecto que indicamos insertando la palabra ref antes de su declaración y a la hora de asignarla.

Es importante tener en cuenta que las referencias locales sólo pueden ser asignadas en el momento de su declaración y, por supuesto, sólo podremos inicializarlas con referencias (en la práctica, expresiones que también van precedidas de la palabra clave ref).

2. Retorno de referencias (ref returns)

Los métodos, al igual que pueden recibir referencias (parámetros ref), en C#7 pueden también retornar referencias. Veamos primer un ejemplo parecido al intercambio de variables que hemos visto anteriormente:
public ref int Max(ref int first, ref int second)
{
if (first >= second)
return ref first;
return ref second;
}
Fijaos que la firma del método contiene varias palabras ref indicando tanto los parámetros de entrada como los de salida son referencias a valores enteros. Lo interesante de este método es que su retorno no es una copia del valor máximo, como sería lo habitual, sino una referencia hacia la variable que contiene el valor máximo.

Existe alguna restricción a tener en cuenta a la hora de retornar referencias, puesto que hay que cumplir unos principios de seguridad básicos. Por ejemplo, no podemos retornar una referencia hacia una variable local porque ésta desaparecerá al finalizar el método; por ello, el retorno consistirá siempre en referencias recibidas como parámetros, o bien hacia miembros que continúen vivos tras su ejecución y no sean candidatos a ser eliminados por el recolector de basura, por ejemplo:
public class MyClass
{
private int IntValue = 0;

public ref int DoSomething(ref int first, ref int second)
{
return ref IntValue;
}
}

3. Cómo se usan juntos

Realmente, estas nuevas capacidades cobran mayor sentido cuando se utilizan juntas. Por ejemplo, en el siguiente código veremos cómo podríamos consumir el método que vimos anteriormente para obtener una referencia al máximo de dos números, y los interesantes daños colaterales que podríamos causar cuando jugueteamos con referencias:
int one = 1, two = 2;
ref var max = ref Max(ref one, ref two);

Console.WriteLine(max++); // 2
Console.WriteLine(two); // 3!!

public static ref int Max(ref int first, ref int second)
{
if (first >= second)
return ref first;
return ref second;
}
Desde el punto de vista sintáctico, lo único que puede llamar la atención es el uso de la palabra clave ref en todo código que opera sobre una referencia. No es que resulte muy cómodo, pero entiendo que es necesario para facilitar la comprensión del código y evitar problemas.

Veamos otro ejemplo, donde se muestra cómo podemos utilizarlo para obtener referencias hacia el elemento de un array. Fijaos de nuevo que el contenido retornado desde el método GetMax() no es una copia del valor máximo, sino una referencia hacia la "caja" donde se guarda éste, por lo que puede ser manipulable con facilidad:
var arr = new int[] {1, 2, 3, 4, 5};
ref var max = ref GetMax(arr);
Console.WriteLine(max); // 5
max*=2;
Console.WriteLine(string.Join(",", arr)); // 1,2,3,4,10
...

ref int GetMax(int[] array)
{
int max = int.MinValue, index = -1;
for (int i = 0; i < array.Length; i++)
{
if (array[i] > max)
{
max = array[i];
index = i;
}
}
return ref array[index];
}
Otro aspecto bastante curioso es que, dado que los retornos de métodos o funciones ref son referencias, podemos operar sobre ellas de forma directa, sin necesidad de almacenarlas en una variable local, lo que puede dar lugar a un código tan sorprendente como el siguiente:
int one = 1, two = 2;
Max(ref one, ref two) = 99;  // WTF?
Console.WriteLine(two);      // 99!!
Pues sí, ¡estamos incluyendo una llamada a un método en el lado izquierdo de una asignación! Aunque algo extraño de leer, si lo pensamos un poco tiene su sentido: el método retorna una referencia, y simplemente estamos usándola para establecer el valor 99 en la variable a la que estamos apuntando.

Y esto es todo…

… al menos de momento, seguro que iremos descubriendo más usos y posibilidades ;D

En definitiva, se trata de un incremento del lenguaje que puede resultar interesante en algunos escenarios poco frecuentes y en cualquier caso muy enfocado al rendimiento, pues estas dos características permiten sustituir movimientos de datos, copias y allocations por simples referencias en memoria a información ya existente. Pero, eso sí, siempre en un entorno seguro y libre de los típicos fallos debidos a punteros desmadrados de lenguajes de menor nivel.

Seguro que no utilizaremos estas novedades todos los días, pero, como en otros casos, es interesante conocerlas y tenerlas en el cinturón de herramientas por si en algún momento necesitamos usarlas. Pero como siempre, antes de lanzaros como posesos a implementar métodos y variables referencia, tened en cuenta las implicaciones que puede tener también a nivel de legibilidad y complejidad en vuestro código; si existe una solución a vuestro problema sin usar ref, probablemente sea mejor optar por ella salvo que tengáis motivos de peso.

Por último, si queréis seguir profundizando en las novedades de C# 7, ahí van links a otros artículos la serie publicados hasta el momento:
Publicado en Variable not found.

» Leer más, comentarios, etc...

Picando Código

Música, Maestro Splinter: Otra noche de música geek

julio 10, 2017 09:00

Música, Maestro SplinterEn 2015 tuve la oportunidad de ser parte de la excelente noche musical: Música, Maestro Yoda. Este año el talentosísimo violinista Gerónimo Oyenard vuelve a liderar un cuarteto de cuerdas para otra noche de música geek. Con el objetivo de que la entrada sea gratuita, el espectáculo se va a financiar a través de una campaña en ColectaTe. A través de ella se puede adquirir varios artículos con arte realizado exclusivamente para el recital por distintos artistas. Les dejo la información, ¡nos vemos ahí!

Miércoles 12 de julio, 19:30 hrs.
Centro Cultural de España
Rincón 629 entre B. Mitre y Juan Carlos Gomez.

Después del suceso del concierto “Música, Maestro Yoda” (2015), el conjunto de cuerdas liderado por el violinista compatriota Geronimo Oyenard se apresta a lanzar su secuela espiritual. Para los que dicen que segundas partes nunca fueron buenas, esta edición incluirá partituras en arreglos originales de memorables bandas sonoras de películas superheroicas, de ciencia ficción, aventuras, animación y series televisivas de ayer, hoy y siempre.

El evento contará con la conducción de los comunicadores Ignacio Alcuri y Leo Lagos, así como también con la ambientación visual compaginada por el programador Fernando Briano. Para completar la presentación, contaremos con arte y souvenirs alusivos para el público, creados para la ocasión por reconocidos artistas de AUCH (Asoc. Uruguaya de Creadores de Historieta).
Los mismos podrán ser adquiridos a modo de recompensas a través de contribuciones en la página web de colectate, así como también en la antesala del concierto en cantidades limitadas o por encargo.

Músicos invitados: Clara Kruk (violín), Mariana Mastrogiovanni (viola), Adrián Borgarelli (violonchelo), Andrea Brassesco (soprano).
Artistas colaboradores: Nicolas Peruzzo, Matias Bergara, entre muchos otros.

La invitación en forma de cómic, por Nicolás Peruzzo.

La invitación en forma de cómic, por Nicolás Peruzzo.

La campaña

A fin de hacer el espectáculo accesible al público general en forma gratuita, la presente colecta tiene como objetivo recaudar fondos suficientes para compensar la labor de arreglos, ensayos y actuación de los músicos, así como también de los citados colaboradores, gastos técnicos y logísticos relacionados con el espectáculo.

Si la colecta superase el monto requerido, el saldo se destinará a la edición 2018 del evento y como donación a AUCH, cuyos artistas brindarán su trabajo en forma honoraria para el material gráfico alusivo. Cada contribuyente es libre de donar el monto que desee, calificando a las siguientes recompensas, con arte de los citados artistas a elección:

a) $ 150: asiento priorizado (pero no garantizado)*
b) $ 250: a) + print
c) $ 300: a) + pin
d) $ 350: a) + taza
e) $ 400: a) + elige tu bis de la siguiente lista! (Imágenes debajo)

  1. A. R. Juele: Spaghetti Western
  2. Nico Peruzzo: Alf-Roos
  3. Edgard Machiavello: Star Trek
  4. Fiorella Santana: Game of Thrones
  5. Gabriel Serra: Mad Max
  6. H. Hansz: James Bond
  7. Maco: Dr. Who
  8. Pablo Praino: Leonardo
  9. Pablo Praino: Splinter
  10. Sebastian Navas: X-Men
  11. Ignacio Alcuri: Star Wars
  12. Matias Bergara: Patoaventuras

Música, Maestro Splinter

d) $ 450: a) + remera

También aceptamos contribuciones menores y mayores que califiquen a dos o más de los ítems mencionados. Los amables donantes serán contactados por correo electrónico para especificar los detalles de su(s) recompensa(s) (diseño a seleccionar, talle de la remera, lista de bises).

*En función de la capacidad limitada de la sala (145 localidades), recomendamos a quienes contribuyan con $ 150 o más se presenten con suficiente anticipación la noche del concierto, a fin de tener un asiento priorizado.

En caso de que la concurrencia masiva al evento deje sin asiento a varios de nuestros contribuyentes, los artistas haremos todo lo posible para agregar una función V.I.P. en otra fecha y locación a determinar.

Auspician: Centro Cultural de España; AUCH; Montevideo Comics; Multiverseros

Campaña en ColectaTe

Les dejo un video de parte de lo que fue la primera edición, ¡nos vemos ahí!

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 290

julio 10, 2017 06:55

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

.NET/.NET Core

ASP.NET/ASP.NET Core

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Otros


Publicado en Variable not found

» Leer más, comentarios, etc...

Blog Bitix

Iniciar rápido un proyecto de Java con Gradle o de Spring con Spring Initializr

julio 09, 2017 11:00

Gradle
Spring
Java

Cuando se empieza un proyecto nuevo desde cero se debe disponer de una herramienta de construcción, una estructura de directorios que siga las convenciones de la herramienta de construcción, añadir las dependencias que vayamos a utilizar y alguna clase que muestre algo al ejecutarlo para comprobar que disponemos de la infraestructura básica de compilación, teses, generación de artefactos e inicio de proyecto.

Dado que las clases Java hay que compilarlas para facilitar la tarea están las herramientas de construcción y estas siguen una serie de convenciones en la estructura de directorios además de requerir algunos archivos. En un proyecto de duración de varios meses o años el tiempo dedicado a crear esta infraestructura básica es despreciable y no complicado si nos basamos en un proyecto similar del que copiar, sin embargo, para hacer alguna prueba rápida es costoso.

En Maven existen los arquetipos que construyen el esqueleto básico del proyecto en base a unas plantillas. En Gradle el equivalente es el plugin init pudiendo elegir crear la estructura de una librería o aplicación Java, Groovy o Scala ejecutable. También se puede elegir el framework para hacer las pruebas automatizadas unitarias, de integración o funcionales.

Al usar el plugin init de Gradle se especifica el tipo de artefacto, aplicación o librería, y el framework para las pruebas unitarias en este caso Spock.

Para proyectos que usen Spring Boot está disponible la herramienta Spring Initializr que en pocos minutos permite disponer de una aplicación funcional con las propiedades que se seleccionen. Se puede elegir la herramienta de construcción, Maven o Gradle, la versión de Spring Boot, los metadatos de la aplicación para el paquete de las clases y artefacto, las dependencias de otros módulos de Spring y otras librerías populares.

Cambiando a la versión completa del generador es posible cambiar el tipo de empaquetado (jar o war), la versión de Java o el lenguaje de programación, además seleccionar aquellas dependencias que inicialmente sean necesarias, muchas son de otros proyectos de Spring. Las dependencias están agrupadas por categorías y van desde seguridad, framework web, motores de plantillas, SQL, NoSQL, numerosas herramientas para la nube, integración con redes sociales, entrada/salida o utilidades para operaciones.

Una vez seleccionadas las opciones se genera el proyecto y descarga un archivo comprimido zip. Con el comando gradlew bootRun si inicia la aplicación, pero dependiendo de las dependencias incluidas quizá sea necesaria hacer alguna configuración adicional antes de poder iniciar la aplicación, por ejemplo si seleccionamos la dependencia de jOOQ hay que definir las propiedades para el DataSource en el archivo application.properties con las que el contenedor inversión de control de Spring pueda crear las conexiones a la base de datos.

Como con todos los generadores de código es recomendable saber suficientemente que es código que generan para en caso de modificaciones posteriores saber como aplicarlas. También es posible que no se adapte exactamente a lo que necesitamos, por ejemplo, si queremos hacer un multiproyecto con Gradle o si una dependencia necesaria no está incluida en la lista de categorías hay añadirla después. A partir de aquí se empieza a programar el proyecto.

En el libro Spring Boot in Action se comenta detalladamente Spring Boot que ofrece múltiples nuevas posibilidades que hace más fácil el desarrollo de un proyecto con Spring. Por otro lado para conocer más detalladamente y ampliamente el ecosistema de Spring está el libro Mastering Spring 5.0 que también inlcuye secciones sobre Spring Boot.

En definitiva el plugin init de Gradle y la utilidad Spring Initializr son unas buenas herramientas para empezar un proyecto Java rápidamente y en pocos minutos disponer de un proyecto básico funcional y listo para implementar la funcionalidad de la aplicación.

» Leer más, comentarios, etc...

Blog Bitix

Introducción a los portales y ejemplo de portlet con Liferay

julio 08, 2017 09:00

Muchas organizaciones usan portales para mantener su presencia en internet. Los portales son herramientas muy versátiles que incluyen la gestión de contenidos y flujo de trabajo para publicarlo, foros, blog, … Liferay es uno de los más conocidos que usa la plataforma Java. La unidad básica funcional de un portal es un portlet que en ciertos aspectos son similares en otros diferentes a lo que son los servlets en las aplicaciones web Java.

Java
Liferay

Los portales son una especialización de un sitio web que presenta información diversa de una forma integrada y uniforme. Suelen aplicarse cuando una entidad tiene necesidades de presentar información según el usuarios autenticado, su rol, los usuarios necesitan colaborar o se necesita integrar información de múltiples fuentes. Son usados por entidades públicas como gobiernos, ayuntamientos y también por corporaciones de tamaño mediano y grande.

Algunos de sus casos de uso son:

Uno de los servidores de portales más destacados y usados es Liferay aunque no es el único siendo Apache Pluto el servidor de referencia. En lo poco que los he probado Liferay comparado con Apache Pluto el primero tarda bastante más en iniciarse, se nota más lento y me ha dado problemas al usar el framework Apache Tapestry para desarrollar un portlet, sin embargo, Liferay incorpora más portlets con multitud de funcionalidades, es más usado y solicitado en ofertas de trabajo. Tanto Liferay como Apache Pluto implementan la especificación de los portlets de Java que son la pieza básica funcional de un portal.

Liferay es el contenedor de portlets y proporciona un entorno de ejecución similar a lo que los contenedores de servlets como Tomcat proporcionan para los servlets. Las similitudes y diferencias entre un servlet y un portlet son las siguientes:

  • Los portlets son gestionados por un contenedor.
  • Su ciclo de vida está gestionado por el contenedor.
  • Generan contenido dinámico.
  • Interactúan con el cliente mediante peticiones y respuestas.

Y se diferencia en que:

  • Los portlets generan únicamente un fragmento de la página web.
  • No están asociados directamente a una URL.
  • No pueden generar contenido arbitrario, si se solicita text/html los portlets deben generar text/html.

El contenedor de portlets proporciona funcionalidades como:

  • Almacenamiento persistente para las preferencias.
  • Procesamiento de solicitudes.
  • Modos de los portlets.
  • Estado de la ventana o fragmento.
  • Información de usuario,

Liferay incluye más de 60 portlets listos para usar que cumplen las funciones de CMS, foros, blogs, agregador de blogs, wiki, calendario, encuestas, anuncios, herramientas sociales, de comercio electrónico, integración de contenido de sistemas externos, geolocalización, tiempo, administración, gestión de flujo de trabajo y otros muchos más ofrecidos en el marketplace.

Desde la página de descargas se puede obtener la edición para la comunidad de Liferay además de otros productos eligiendo la versión deseada y en la red para desarrolladores obtener documentación y material de referencia. Una vez descargado el archivo de la distribución de Liferay y descomprimido se inicia con el comando ubicado en tomcat-8.0.32/bin/startup.sh. En el archivo tomcat-8.0.32logs/catalina.out se emiten las trazas y mensajes del servidor. Iniciado Liferay se presenta una página de configuración, se han de aceptar los términos y condiciones e iniciar sesión con el usuario creado en la primera página de configuración.

Para añadir un portlet propio a Liferay hay que acceder al Panel de control > Aplicaciones > Gestor de aplicaciones y pulsar la opción cargar ubicada en la parte superior derecha de la página. En la salida del servidor aparecerán varias trazas relativas al despliegue del portlet.

Los portlets se distribuyen por lo general como archivos de aplicaciones web .war con varios descriptores adicionales con información que usa Liferay para el despliegue del portlet.

En el siguiente ejemplo comentaré cómo crear un portlet Hola Mundo sin ayuda de ningún framework como Spring o Apache Tapestry aunque Liferay proporciona ayuda y documentación para desarrollarlos con Liferay MVC Portlet o Spring MVC.

El archivo descriptor principal es portlet.xml donde se describen los portlets de la aplicación indicando por ejemplo su nombre, la clase que lo implementa o los modos que soporta, otros archivos descriptores son web.xml, liferay-portlet.xml y liferay-display.xml con unas propiedades exclusivas de Liferay indicando el icono y la categoría en la que ubicar el portlet en la paleta de portlets.

Un portlet es una clase Java que extiende de GenericPortlet. En el caso del ejemplo es muy sencillo ya que solo emite un mensaje usando una preferencia de configuración que Liferay se encarga de persistir, tiene un modo de edición y procesa una acción para cambiar el valor de una preferencia que se utiliza al emitir el mensaje.

Los portlets con sus diferencias funcionales con los servlets tienen muchas similitudes y una API con clases equivalentes a los servlets. Así la clase principal de la que hay que heredar para crear un portlet es GenericPortlet o implementar la interfaz Portlet. Las peticiones en los portlets siguen una serie de fases que se van ejecutando en el siguiente orden ActionPhase, EventPhase, HeaderPhase y RenderPhase. Para los recursos como imágenes o documentos hay una fase específica ResourcePhase.

Fases y métodos del ciclo de vida de un portlet

Para cada una de estas fases en la API de los portlets hay un método específico que son processAction, procesEvent, renderHeaders y render. Los portlets poseen modos que se visualizan con los métodos doEdit, doHelp y doView o el correspondiente anotado con @RenderMode. Cada uno de esos métodos para cada una de las fases reciben dos parámetros uno que representa a la petición que heredan de PortletRequest y son ActionRequest, ClientDataRequest, EventRequest, HeaderRequest, RenderRequest y ResourceRequest. Los objetos que representan a las respuestas heredan de PortletResponse y son ActionResponse, EventResponse, HeaderResponse, MimeResponse, RenderResponse, ResourceResponse y StateAwareResponse.

La interfaz PorletPreferences obtenida con el método getPreferences() de una clase que herede de PortletRequest también es importante ya que con ella el portlet es capaz de persistir incluso entre reinicios del servidor los datos relativos a su funcionamiento que desee aunque esto no sustituye a la utilización de una base de datos como PostgreSQL o MongoDB. Los portlets también tienen el equivalente de filtros de los servlets con la clase PortletFilter y el equivalente de sesión con la clase PortletSession.

Usando como herramienta de construcción del proyecto Gradle el archivo .war a desplegar el Liferay se genera con la tarea build en el directorio build/libs/HolaMundoPortlet-0.1.war. Esta archivo hay que desplegarlo en Liferay para posteriormente incluirlo en alguna página, se visualice el contenido que genera y se pueda interactuar con él.

Desarrollar un portlet con su API directamente es una tarea costosa si la funcionalidad o complejidad del portlet es mucha. Al igual que en Java no se suele utilizar la API de los servlets directamente, aunque es la API subyacente, y se suele utilizar alguno de los muchos frameworks disponibles para los portlets también hay varios frameworks entre los que elegir. En el artículo Portlets con el framework Apache Tapestry y Apache Pluto muestro un ejemplo usando un framework de alto nivel, orientado a componentes y altamente productivo.

Aunque el libro Liferay in Action o Portlets in Action no están actualizados a la última versión sirven para conocer los conceptos básicos de su funcionamiento, explican la teoría e incluyen ejemplos de código de como crear un portlet.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew build.

» Leer más, comentarios, etc...

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Sponsors

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin