Weblogs Código

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...

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

Volkov Commander

julio 05, 2017 03:41

Podríamos decir que Norton Commander, o Comandante Norton, fueron los que iniciaron el mercado de los gestores de archivos de dos paneles. Serían los padres de Midnight Commander, PC-Tools, Norton Desktop o FAR, y los abuelos de Total Commander, Q-Dir o SpeedCommander. Hablamos de 1986, cuando aparecía Norton Commander (NC) de Peter Norton Computing, y […]

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

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

Poesía Binaria

Píldora #Bash: Conocer el tiempo que lleva un proceso abierto en Linux

julio 05, 2017 08:20

Bash tiempo proceso
¡Una nueva sección! Después de tanto tiempo y tantas secciones inacabadas. Algo que tenía ganas de hacer, es crear posts con esa serie de recetas, scripts, oneliners, o píldoras que se me van ocurriendo mientras trabajo en consola. Suelen ser cosas pequeñas y deberían caber en un tweet junto con una pequeña descripción. De todas formas a mí me gusta explicar un poco más las cosas para que quede bien claro.

En este caso, vamos a empezar conociendo el tiempo que lleva abierto un proceso, o un programa. Puede ser una información útil si, por ejemplo, tenemos corriendo servicios que ante un problema se reinician solos. O porque simplemente queremos saber ese dato, al igual que: ¿cuánto tiempo llevo con el ordenador encendido? que podemos responder con uptime. En este caso queremos saber cuánto tiempo llevo con un programa arrancado, como puede ser firefox, spotify, chrome, o apache.

Al grano

Vamos a poner dos métodos que iré explicando más adelante:

pid=$(pidof firefox); s=$(($(date +%s)-$(stat -c %Y /proc/”$pid”))); echo $(date -ud “@$s” +”$(($s/86400)) dias %Hh:%Mm:%Ss”)
1 dias 23h:39m:24s

O de una forma más corta, podemos utilizar

ps -eo pid,etime,comm | grep firefox
24441  1-23:53:14 firefox

Donde veremos primero el PID, luego el tiempo y luego el nombre del proceso.

Explicando el primero

En el primer ejemplo, lo primero que hacemos es coger el ID del proceso (PID).

pid=$(pidof firefox)

Tras ello, sacaremos la fecha y hora en formato UNIX del momento actual (como siempre, esto será el número de segundos desde el 1 de Enero de 1970 a las 00:00).
date +%s

Luego consultaremos la fecha del directorio /proc/PID y la pondremos en formato UNIX también. Recordemos que todos los procesos tendrán un directorio virtual en /proc/ con información sobre los mismos.
stat -c %Y /proc/”$pid

El objetivo es restar las dos fechas, así obtenemos el número de segundos transcurridos desde que se inició el proceso. .
s=$(($(date +%s)-$(stat -c %Y /proc/”$pid”))); echo $s

Ahora sólo queda ponerlo en un formato más humano, es decir, en días, horas, minutos y segundos. Para ello, el número de días lo podemos sacar a mano, ya que sería $s (el número de segundos) dividido entre 86400 (los segundos que tiene un día). Pero el resto se lo podemos pedir al comando date. Lo hacemos así porque date presenta fechas y horas (1 de Enero de 2017 a las 12:34:45) no tiempos transcurridos (120 días 3 horas, 4 minutos, 5 segundos), aunque la hora actual es lo mismo que pedir el tiempo transcurrido desde las 00:00.
Podríamos también calcular todos los elementos a mano (días, horas, minutos y segundos), el problema es que el script ya no entraba en un tweet:

pid=$(pidof firefox); secs=$(($(date +%s)-$(stat -c %Y /proc/”$pid”))); printf ‘%d days %dh:%dm:%ds\n’ $(($secs/86400)) $(($secs%86400/3600)) $(($secs%3600/60)) $(($secs%60))
1 dias 23h:39m:24s

El segundo ejemplo, con ps

Este segundo ejemplo, será mucho más portable entre sistemas UNIX, y mucho más corto, es verdad, pero no nos da mucho juego a la hora de escoger el formato de salida. El formato del tiempo será fijo (días-horas:minutos:segundos) y será importante visualizar el PID o el nombre del proceso.
Además, es mucho más lento, ya que con ps estamos analizando el tiempo de todos los procesos del sistema, aunque luego en pantalla sólo mostremos el que nos interesa porque la salida de ps la hemos pasado por grep.

The post Píldora #Bash: Conocer el tiempo que lleva un proceso abierto en Linux appeared first on Poesía Binaria.

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

Poesía Binaria

¿Cómo cerrar un puerto TCP ocupado por una aplicación en GNU/Linux?

julio 04, 2017 08:41

Cuando vamos a establecer una comunicación entre dos máquinas a través de una red TCP/IP, vamos lo que estamos haciendo a diario miles de veces con nuestro ordenador mientras navegamos por Internet, lo hacemos a través de un puerto. Imaginemos que tenemos un sistema de comunicación rudimentario entre 10 amigos, con 5 cables y, cada cable, nos permite hablar con uno de nuestros amigos, pero claro, como sólo tenemos 5 cables, sólo podemos hablar con 5 amigos al mismo tiempo, así que en algún punto del recorrido deberá haber alguien con la capacidad de enchufar y desenchufar esos cables. El caso es que esos cables serían los canales, o puertos de nuestro sistema, alguien deberá enchufar y desenchufar cables para asegurarse de que nos conectemos con quien queremos.
Por lo tanto, yo tengo 5 cables que corresponden con las 5 conexiones que puedo tener simultáneas, ahora, por seguir un orden, si quiero conectarme con Alberto, éste se conectará con mi canal 1, aunque nadie me dice a mí que yo también esté en su canal 1. Ahora bien, conecto con Bruno por el canal 2 y cierro a Alberto, para conectar con Carla, que podemos enchufarla en el 1, ya que está libre… ahora Bruno y Alberto contactarán entre ellos, cada uno por alguno de los canales que tenga libre. Bueno, ¿se capta la idea?

Hablando de TCP/IP tenemos algunos más de 5 puertos (podríamos tener 65535 puertos) y serían las conexiones que podemos tener abiertas al mismo tiempo desde una máquina, o al menos desde una misma dirección IP. Y, hablando de nuestro ordenador, cada programa que tenemos en ejecución y que hace conexiones de red puede estar utilizando determinados puertos para establecer las comunicaciones. Por ejemplo, nuestro navegador, programas de chat, correo, música online, sincronización de información y demás tendrán ciertos puertos abiertos para utilizar dichos servicios. Algunos puertos estarán abiertos en modo escucha (para que otros se conecten con nosotros) y otros puertos serán las conexiones establecidas (nos hemos conectado a un puerto en modo escucha, en nuestro equipo, o en un equipo de la red).

Entonces, las aplicaciones que corren en nuestro sistema pueden tener determinados puertos abiertos para realizar su trabajo.

Nos gusta el cacharreo

Ahora bien, puede que si estamos desarrollando un programa que haga uso de la red, nuestro programa deje un puerto abierto y se haya quedado bloqueado. O también puede ser que estemos intentando poner a prueba la gestión de errores de un software, o estemos haciendo algo de ingeniería inversa. En cualquier modo, lo que queremos es cerrar sin piedad un puerto en nuestro ordenador.
Tenemos que tener cuidado porque, si lo hacemos sin control, puede que algún programa haga cosas muy raras. Además, necesitaremos permisos de root para algunas cosas, por lo que serán acciones privilegiadas.
En mi caso, casi siempre que hago esto es para experimentar. Por ejemplo, ¿cómo se comportaría mi programa si cierro la conexión de repente? Sin que ninguna de las dos partes se lo espere. Como también está bien utilizar iptables para restringir las futuras comunicaciones de mi aplicación (sería algo así como tirar del cable). El objetivo es que nuestro software sea más rubusto y sepa qué hacer en este tipo de casos.

Ver qué programa utiliza qué puerto

Lo primero que debemos hacer es averiguar qué proceso está utilizando el puerto que queremos. Con netstat.

netstat -tanp
(No todos los procesos pueden ser identificados, no hay información de propiedad del proceso
no se mostrarán, necesita ser superusuario para verlos todos.)
Conexiones activas de Internet (servidores y establecidos)
Proto  Recib Enviad Dirección local         Dirección remota       Estado       PID/Program name
tcp        0      0 127.0.0.1:631           0.0.0.0:*               ESCUCHAR    --
tcp        0      0 0.0.0.0:45847           0.0.0.0:*               ESCUCHAR    18904/nc
tcp        0      0 0.0.0.0:17500           0.0.0.0:*               ESCUCHAR    19556/dropbox
tcp        0      0 127.0.0.1:4381          0.0.0.0:*               ESCUCHAR    30420/spotify
tcp        0      0 192.168.0.7:60892       64.233.167.188:5228     ESTABLECIDO 22144/libpepflashpl
tcp        0      0 192.168.0.7:42090       157.55.235.157:40008    ESTABLECIDO 1992/skype
tcp        0      0 192.168.0.7:39342       xx.xxx.xxx.xx:28         ESTABLECIDO 7428/ssh
tcp     3775      0 127.0.0.1:35381         127.0.0.1:37516         ESTABLECIDO --
tcp        0      0 192.168.0.7:48130       192.30.253.124:443      ESTABLECIDO 24441/firefox
tcp        0      1 192.168.0.7:33380       92.189.149.89:35148     FIN_WAIT1   --
tcp        0      1 192.168.0.7:33988       92.189.149.89:35148     FIN_WAIT1   --
tcp        0      1 192.168.0.7:39694       37.35.181.204:63905     FIN_WAIT1   --
tcp        0      0 192.168.0.7:60244       xx.xxx.xxx.xx:143        ESTABLECIDO 3708/thunderbird
tcp        0      0 192.168.0.7:60544       138.68.115.83:443       TIME_WAIT   --
tcp        0      0 127.0.0.1:50542         127.0.0.1:35381         ESTABLECIDO 7345/emacs
tcp        0      0 192.168.0.7:53492       72.21.206.121:443       ESTABLECIDO 24441/firefox
tcp        0      0 127.0.0.1:37516         127.0.0.1:35381         ESTABLECIDO 7345/emacs

Sólo he puesto una pequeña muestra. Con los argumentos tanp conseguimos:
  • -t: Sólo muestra conexiones TCP.
  • -a: Muestra también los puertos que estamos escuchando
  • -n: No resuelve las direcciones. (Es mucho más rápido, pero veremos sólo IPs y no nombres)
  • -p: Muestra los procesos que originan dichas conexiones. (pid y nombre, si es posible)

Vemos que muchos programas no aparecen y eso puede ser debido a los privilegios con los que hemos ejecutado el programa, por lo que si utilizamos sudo deberíamos ver todos o casi todos. Por supuesto, la salida de netstat la podemos pasar por grep, para realizar un filtrado de lo que nos interesa:

netstat -tanp | grep  40008
tcp        0      0 192.168.0.7:42090       157.55.235.157:40008    ESTABLECIDO 1992/skype

Si queremos más información sobre las conexiones abiertas y su asociación con procesos, en lugar de netstat podremos utilizar lsof. Aunque con lsof veremos muchísima información, ya que no sólo se centra en conexiones de red, sino que veremos cada descriptor de archivo abierto en el sistema. Y como las conexiones de red suelen tener un descriptor de archivo asociado, las veremos también . Por lo que, con lsof, podremos visualizar todos los descriptores abiertos por todos los procesos. Por ejemplo:

sudo lsof -n | grep emacs | grep TCP
emacs      7345                     gaspy   17u     IPv4           40529162       0t0        TCP 127.0.0.1:49778->127.0.0.1:35381 (ESTABLISHED)
emacs      7345                     gaspy   18u     IPv4           40538567       0t0        TCP 127.0.0.1:50542->127.0.0.1:35381 (ESTABLISHED)
emacs      7345                     gaspy   19u     IPv4           40585112       0t0        TCP 127.0.0.1:53658->127.0.0.1:35381 (ESTABLISHED)
emacs      7345                     gaspy   20u     IPv4           40595540       0t0        TCP 127.0.0.1:53728->127.0.0.1:35381 (ESTABLISHED)
emacs      7345                     gaspy   21u     IPv4           41279086       0t0        TCP 127.0.0.1:37436->127.0.0.1:35381 (ESTABLISHED)
emacs      7345                     gaspy   22u     IPv4           41283195       0t0        TCP 127.0.0.1:37516->127.0.0.1:35381 (ESTABLISHED)
emacs      7345                     gaspy   23u     IPv4           41284210       0t0        TCP 127.0.0.1:37578->127.0.0.1:35381 (ESTABLISHED)

Con lo que veremos las conexiones TCP que ha establecido mi aplicación Emacs.

También podemos conocer con fuser, el proceso que origina la conexión. Conociendo el puerto (por ejemplo 35381):

fuser -n tcp 35381
35381/tcp:            7359

Matando procesos

Si queremos ser rápidos, lo más fácil para cerrar la conexión de manera abrupta, y una buena forma para probar nuestros programas es matarlos. Como muchos sabréis podemos hacerlo con kill.

kill 7345

O con killall, pero tenemos que tener cuidado si hay varios procesos con el mismo nombre.
killall emacs

Aunque tanto kill como killall, por defecto intentan cerrar la aplicación educadamente, y la aplicación puede elegir cerrarse o no. Incluso puede estar bloqueada, por lo que si queremos podemos utilizar el argumento -9 o -SIGKILL , con lo que el programa no tendrá más remedio que terminar, es más, no tiene elección, ya que es el sistema operativo el que lo mata.

De todas formas, una forma algo más segura es matar al proceso que tiene un determinado puerto. Igual que antes veíamos la información con fuser. Este comando también mata aplicaciones, directamente enviando SIGKILL, es decir, matando, sin pedir permiso, las aplicaciones (a no ser que lo especifiquemos en los argumentos):

fuser -k -n tcp 8080
8080/tcp:            23003

Método artesano que sólo cierra conexiones

Me encanta este método. Con él hacemos que el proceso entre en depuración y lo matamos. Una cosa importante a tener en cuenta, como dije antes, son los descriptores. Cada conexión, o fichero abierto tiene un descriptor único para la aplicación en la que se encuentra. Y suelen ser números pequeños y secuenciales. Así como la salida estándar por pantalla suele ser el 1, la salida de error el 2, y la entrada por teclado el 0, cuando abrimos el primer fichero, se le asignará el 3 y cuando creamos una conexión después el 4, y así sucesivamente. Entonces, veamos cómo al ejecutar lsof, vemos en mi cuarta columna, generalmente unos números. Para hacer el ejemplo más legible, he ejecutado netcat para abrir un puerto, así:

nc -l 54321

Luego, para averiguar el ID de proceso, lo podemos hacer con fuser, por ejemplo, aunque podemos utilizar cualquier comando de los explicados anteriormente:
fuser -n tcp 54321
54321/tcp:           23101

Ya que tenemos nuestro PID, 23101, vemos los descriptores abiertos por ese proceso:
lsof -np 23101
COMMAND   PID  USER   FD   TYPE    DEVICE SIZE/OFF    NODE NAME
nc      23101 gaspy  cwd    DIR      8,22    12288 7602177 /home/gaspy
nc      23101 gaspy  rtd    DIR      8,18     4096       2 /
nc      23101 gaspy  txt    REG      8,18    31248  129397 /bin/nc.openbsd
nc      23101 gaspy  mem    REG      8,18  1868984  426046 /lib/x86_64-linux-gnu/libc-2.23.so
nc      23101 gaspy  mem    REG      8,18   101200  400728 /lib/x86_64-linux-gnu/libresolv-2.23.so
nc      23101 gaspy  mem    REG      8,18    81040  393967 /lib/x86_64-linux-gnu/libbsd.so.0.8.2
nc      23101 gaspy  mem    REG      8,18   162632  396890 /lib/x86_64-linux-gnu/ld-2.23.so
nc      23101 gaspy    0u   CHR    136,14      0t0      17 /dev/pts/14
nc      23101 gaspy    1u   CHR    136,14      0t0      17 /dev/pts/14
nc      23101 gaspy    2u   CHR    136,14      0t0      17 /dev/pts/14
nc      23101 gaspy    3u  IPv4 139144920      0t0     TCP *:54321 (LISTEN)

Al final, veremos 0u, 1u, 2u, 3u (la u indica que es de lectura/escritura; r, sería lectura y w, escritura. Aunque algunas veces tenemos descriptores de lectura/escritura que no son necesariamente así… pero eso es otra historia). Lo que nos interesa es que el descriptor 3 es el que corresponde con la conexión que queremos cerrar. Ahora, podemos utilizar gdb para depurar el programa e introducir un comando close() a dicho descriptor, para cerrarlo (salen muchas letras, pero es sencillo):

sudo gdb -p 23101
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type “show copying”
and “show warranty” for details.
This GDB was configured as “x86_64-linux-gnu”.
Type “show configuration” for configuration details.
Para las instrucciones de informe de errores, vea:
.
Find the GDB manual and other documentation resources online at:
.
For help, type “help”.
Type “apropos word” to search for commands related to “word”.
Adjuntando a process 23101
Leyendo símbolos desde /bin/nc.openbsd…(no se encontraron símbolos de depuración)hecho.
Leyendo símbolos desde /lib/x86_64-linux-gnu/libbsd.so.0…(no se encontraron símbolos de depuración)hecho.
Leyendo símbolos desde /lib/x86_64-linux-gnu/libresolv.so.2…Leyendo símbolos desde /usr/lib/debug//lib/x86_64-linux-gnu/libresolv-2.23.so…hecho.
hecho.
Leyendo símbolos desde /lib/x86_64-linux-gnu/libc.so.6…Leyendo símbolos desde /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.23.so…hecho.
hecho.
Leyendo símbolos desde /lib64/ld-linux-x86-64.so.2…Leyendo símbolos desde /usr/lib/debug//lib/x86_64-linux-gnu/ld-2.23.so…hecho.
hecho.
0x00007ff39bffa060 in __accept_nocancel () at ../sysdeps/unix/syscall-template.S:84
84 ../sysdeps/unix/syscall-template.S: No existe el archivo o el directorio.
call close(3)
1 = 0
quit
Una sesión de depuración está activa.
Inferior 1 [process 23355] will be detached.
¿Salir de cualquier modo? (y or n)
y
Separando desde el programa: /bin/nc.openbsd, process 23355

Eso sí, puede que el programa que estamos corriendo dependa de la conexión, o ese sea su único propósito, por ejemplo netcat o telnet, por lo que una vez cerrada la conexión se cierran. Aunque otros programas utilizan las conexiones de red realizan otras tareas como por ejemplo mantener un entorno gráfico, monitorizar el sistema, actualizar un fichero, etc; deberían poder gestionar correctamente ese cierre de conexión sin sufrir ningún desastre.

killcx, o cerrar conexiones educadamente

Ésta es una de esas herramientas (escrita en Perl) que deben estar en nuestra caja de herramientas de sysadmin. Y sirve para matar conexiones, aunque lo hace de un modo seguro. Es más, es capaz de cerrar una conexión que se ha quedado bloqueada en el sistema. Para utilizarla debemos hacer:

sudo ./killcx 127.0.0.1:54321 lo
killcx v1.0.3 -- (c)2009-2011 Jerome Bruandet -- http://killcx.sourceforge.net/
[PARENT] checking connection with [127.0.0.1:54321]
[PARENT] found connection with [127.0.0.1:44904] (ESTABLISHED)
[PARENT] forking child
[CHILD]  setting up filter to sniff ACK on [lo] for 5 seconds
[PARENT] sending spoofed SYN to [127.0.0.1:44904] with bogus SeqNum
[CHILD]  hooked ACK from [127.0.0.1:44904]
[CHILD]  found AckNum [3556219343] and SeqNum [477005426]
[CHILD]  sending spoofed RST to [127.0.0.1:44904] with SeqNum [3556219343]
[CHILD]  sending RST to remote host as well with SeqNum [477005426]
[CHILD]  all done, sending USR1 signal to parent [23768] and exiting
[PARENT] received child signal, checking results…
=> success : connection has been closed !

La IP y puerto variarán según nuestras necesidades, y lo es el dispositivo, que podrá ser eth0, wlan0, etc.
La herramienta podemos descargarla desde aquí.

Foto principal: Alex Lehner

The post ¿Cómo cerrar un puerto TCP ocupado por una aplicación en GNU/Linux? appeared first on Poesía Binaria.

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

Poesía Binaria

Programando ATtinys para miniaturizar nuestros proyectos electrónicos

julio 04, 2017 08:16

Un Arduino, ¡es muy grande! Es una placa que luego no sabes dónde meter, un circuito integrado que ocupa mucho y en algunas ocasiones no usamos la miad de las entradas y salidas. Así que, para pequeños montajes que requieran algo de lógica de programación o comunicación que debamos meter en una pequeña placa sin complicarnos mucho la vida, y hemos trabajado antes con la plataforma Arduino. Un buen siguiente paso es comprar un pequeño ATtiny, un circuito integrado que cuesta alrededor de 2€. Ya que tiramos de ATtiny, vamos a dedicarnos al ATtiny84 y ATtiny85 que son los más grandes de la gama.

ATtiny85


Este circuito cuenta con:

  • 8 patillas
  • Funciona desde 1.8V a 5V
  • 300uA a 1MHz y 1.8V
  • Frecuencia de operación de 20MHz como máximo si utilizamos un oscilador externo. Aunque puede funcionar entre 1MHz y 8MHz con el propio oscilador interno. Ideal para operaciones de bajo consumo.
  • 8KB de memoria Flash para nuestros programas.
  • 512bytes de EEPROM para almacenar constantes o valores de forma permanente. EEPROM es una memoria de escritura limitada, así que no es conveniente para almacenar datos que vayan cambiando mucho.
  • 0.5Kb de SRAM. Como memoria temporal para nuestros programas.
  • 6 canales GPIO, para interactuar con el exterior.
  • 4 de ellos soportan entrada analógica a través de un conversor analógico-digital de 10bit.
  • 5 de ellos soportan PWM

Y muchas más cosas que iremos descubriendo y experimentando.

ATtiny84


Este circuito cuenta con:

  • 14 patillas
  • Funciona desde 1.8V a 5V
  • 300uA a 1MHz y 1.8V
  • Frecuencia de operación de 20MHz como máximo si utilizamos un oscilador externo. Aunque puede funcionar entre 1MHz y 8MHz con el propio oscilador interno. Ideal para operaciones de bajo consumo.
  • 8KB de memoria Flash para nuestros programas.
  • 512bytes de EEPROM para almacenar constantes o valores de forma permanente. EEPROM es una memoria de escritura limitada, así que no es conveniente para almacenar datos que vayan cambiando mucho.
  • 0.5Kb de SRAM. Como memoria temporal para nuestros programas.
  • 12 canales GPIO, para interactuar con el exterior.
  • 8 de ellos soportan entrada analógica a través de un conversor analógico-digital de 10bit.
  • 4 de ellos soportan PWM

Configurando el entorno Arduino

El método más sencillo para hacer la programación es con el entorno Arduino, que a estas alturas de 2017 va por la versión 1.8.3 y tiene muchas cosas más que cuando empecé. Así que vamos a configurar nuestro entorno para que soporte los chips ATtiny. Para ello vamos a Archivo / Preferencias y en la línea Gestor de URLs adicionales de tarjetas pegamos la siguiente dirección: https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json:

Ahora bien. Para relizar la programación del circuito integrado, voy a utilizar mi propia placa Arduino. Por lo tanto voy a instalar un programador en mi Arduino que se encargará de escribir la memoria del ATtiny. Para ello, seleccionamos nuestro Arduino, el puerto donde está conectado y seleccionamos Archivo / Ejemplos / ArduinoISP. Una vez abierto el programa lo subimos a nuestra placa Arduino.

Seguidamente vamos a Herramientas / Placa / Gestor de tarjetas, nos aparecerá una ventana como la siguiente, donde podemos escribir attiny en la barra de búsqueda. Nos aparecerá la configuración para incluir estos circuitos integrados a nuestro entorno Arduino. Así que seleccionamos la última versión y pulsamos Instalar:

Ahora podremos seleccionar nuestro ATtiny en Herramientas / Placa / ATtiny. Para este ejemplo vamos a programar un ATtiny85, aunque colocando los cables correctamente podremos programar también los ATtiny84 o incluso los Atmega.

Ahora seleccionamos el resto de opciones dentro de herramientas:

  • Procesador. Donde especificamos el chip que vamos a programar.
  • Clock. Donde decimos la velocidad de reloj. Estos ATtiny tienen un reloj interno que puede ir hasta los 8MHz, aunque parece que con algunos modelos puede llegar hasta 16MHz, aunque a mayor frecuencia, mayor consumo eléctrico. Además, estos chips soportan una fuente de reloj externa hasta 20MHz.
  • Puerto. Mi USB está en /dev/ttyUSBx (con la placa Duemilanove) o en /dev/ttyACMx (con Arduino Uno), usando GNU/Linux. O tal vez en otros sistemas COM3/COM4… ya depende de cómo esté enchufado el dispositivo.
  • Programador: Arduiono as ISP. Le estamos diciendo que el propio Arduino va a actuar como programador del chip.

Montando el circuito para programar nuestro ATtiny

Debemos montar algo así:

En este circuito conectamos los pins:

Arduino PIN 10 –> ATTiny Reset PIN
Arduino PIN 11 –> ATTiny PIN 0
Arduino PIN 12 –> ATTiny PIN 1
Arduino PIN 13 –> ATTiny PIN 2
Arduino GND PIN –> ATTiny PIN GND PIN
Arduino +5V PIN –> ATTiny PIN VCC PIN


Los diodos led, los he conectado como indicación (no es necesario tenerlos, y me podéis matar por no colocar resistencias, pero están muy chulos):

  • PIN 9 – Es el heartbeat. Indica que nuestra placa Arduino como programador funciona y está lista
  • PIN 8 – Error.
  • PIN 7 – El programador se está comunicando con el ATtiny

NOTA: En las placas Leonardo, Yun y seguro que algunas otras utilizan el puerto ICSP para realizar la programación del chip. Como se indica a continuación (aunque no lo he probado. Si alguien se atreve que me deje un comentario):

Arduino PIN 10 –> ATTiny Reset PIN
Arduino ICSP MOSI PIN –> ATTiny PIN 0
Arduino ICSP MISO PIN –> ATTiny PIN 1
Arduino ICSP SCK PIN –> ATTiny PIN 2
Arduino GND PIN –> ATTiny PIN GND PIN
Arduino +5V PIN –> ATTiny PIN VCC PIN

El condensador entre RESET y GND evita que Arduino se reinicie actuando como ISP. De todas formas muchas veces no es necesario poner el condensador. Depende de la placa que estemos usando. Si no tenéis ninguno a mano, intentadlo sin él.

Programando el chip

Lo primero será cargar un programa, como por ejemplo el Blinking Led, ese ejemplo típico, aunque por defecto el led suele estar conectado al Pin13, y en Attiny no tenemos tantos, así que podemos conectarlo al 0 o al 1 para hacer la prueba. O si no, podemos utilizar mi versión modificada que utiliza la salida analógica, ya que el Pin 0 del Attiny85 soporta PWM, vamos a hacer algo con él:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define MY_PIN 0
#define ESPERA 40
#define INCREMENTO 20

int level = 0;
int direction= INCREMENTO;

void setup() {
  pinMode(MY_PIN, OUTPUT);
}

void loop() {
  level+=direction;
 
  if (level > 255 || level < 0)
  {
    direction*=-1;
    level+=direction;
  }
  analogWrite(MY_PIN, level);  
  delay(ESPERA);
}

Programando ATtiny84

Para programar este circuito, tendremos que conectar nuestra placa Arduino al chip. Para ello, debemos conectar VCC, GND y reset a los sitios correspondientes, y luego los pins MOSI, MISO y SCK. De la siguiente forma:

Arduino PIN 10 –> ATTiny Reset PIN
Arduino PIN 11 –> ATTiny PIN 7
Arduino PIN 12 –> ATTiny PIN 8
Arduino PIN 13 –> ATTiny PIN 9
Arduino GND PIN –> ATTiny PIN GND PIN
Arduino +5V PIN –> ATTiny PIN VCC PIN

Ahora, con nuestro ATtiny conectado, le damos a Subir y listo! Se compilará y se subirá a nuestra placa.

The post Programando ATtinys para miniaturizar nuestros proyectos electrónicos appeared first on Poesía Binaria.

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

Variable not found

Literales binarios y separador de dígitos numéricos en C#7

julio 04, 2017 06:55

C#Poco a poco seguimos desmenuzando las novedades disponibles tras la llegada de C# 7. Hasta el momento hemos visto las siguientes:
En esta ocasión veremos un par de pequeñas adiciones al lenguaje que, aunque de mucho menor calado de otras que ya hemos repasado, también merecen tener su minutillo de protagonismo ;)


En primer lugar, C# 7 permite el uso directo de literales binarios en el código. Al igual que para literales hexadecimales solemos usar el prefijo "0x", en el caso de los binarios utilizaremos "0b" como en el siguiente ejemplo:
var binary = 0b101010111100110111101111;
Observemos ahora el código anterior y hagámonos una sencilla pregunta: ¿cuántos dígitos hemos escrito en el literal binario anterior? O ¿Qué bits corresponden al nibble inferior del segundo byte menos significativo de esa contante?

Las preguntas no tienen una sencilla respuesta porque la legibilidad de ese tipo de constantes es bastante escasa debido a su longitud. Y esto nos lleva a la siguiente mejora del lenguaje: el separador de dígitos numéricos.

La idea, tomada de otros lenguajes como Java, Python, Ruby o el clásico Ada, consiste en permitir el uso del carácter guión bajo "_" como separador en literales numéricos con el objetivo de mejorar la legibilidad de código en porciones que definen constantes o valores.

Revisemos de nuevo el ejemplo anterior, ahora utilizando este carácter separador. Mucho más claro, ¿verdad?
var binary = 0b1010_1011_1100_1101_1110_1111;
Este separador puede usarse en cualquier tipo de constante numérica. El lenguaje no utiliza ninguna asunción en cuanto al significado de los grupos en los que queda separada la constante, simplemente ignorará los underscore que se encuentre durante el parseo:
var dec = 999_999;
var hex = 0xFF_FF_FF;

Console.WriteLine(123_456 == 1_2_3_4_5_6); // True
Console.WriteLine(0b1111_1111_1111_1111 == 0xFF_FF); // True
Así de simple :)

Publicado en Variable not found.

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

Adrianistán

Documentación con rustdoc

julio 03, 2017 03:28

La documentación es una parte importante y muchas veces olvidada de un proyecto de software. Rust cuenta desde el principio con una herramienta destinada a que escribir documentación sea poco doloroso, se trata de rustdoc.

Comentarios en Markdown

Los comentarios de rustdoc empiezan con 3 barras y pueden llevar Markdown. Se escriben encima de la función, estructura, etc que describamos.

/// Perfil almacena los datos del perfil de un usuario en nuestra webapp
struct Perfil{
    username: String,
    password: String,
    url: Option<String>
}

impl Perfil{
    /// Genera un nuevo Perfil
    /// # Ejemplo
    /// ```
    /// let user = Perfil::new("The42","1234");
    /// ```
    pub fn new(u: &str, p: &str) -> Perfil{
        Perfil {username: String::from(u), password: String::from(p), url: None}
    }
}

Mencionar que el código del comentario es sometido a tests también por cargo test. Para generar la documentación basta con escribir cargo doc y Cargo generará la documentación en formato HTML.

Consultando documentación

El mejor sitio para leer la documentación de Rust es Docs.rs. Docs.rs ejecuta cargo doc a todas las crates de Crates.io y las expone al público sin costo.

 

La entrada Documentación con rustdoc aparece primero en Blog - Adrianistan.eu.

» 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