diff --git a/.gitignore b/.gitignore
index 5761abcfdf0c26a75374c945dfe366eaeee04285..31260f1fedc2469e76bbf4c24568ebf1f0b58f7d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,12 @@
-*.o
+doc/test_main_ex
+doc/info_reader_ex
+doc/mojitos.1
+tests/run
+src/counters_option.h
+src/sensors.h
+sensors.mk
+bin
+obj
+*.swp
+*.swo
+.vscode
diff --git a/AUTHORS b/AUTHORS
index 0bfc663bd4cba8c78a3de80d903028b587a3f338..13ddd11d874c2c46bb4f6792525e0d3a02371bb4 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1,4 @@
-Georges Da Costa georges.da-costa@irit.fr
\ No newline at end of file
+Georges Da Costa georges.da-costa@irit.fr
+Floréal Risso floreal.risso@univ-tlse3.fr
+Alexis Paronnaud alexis.paronnaud@univ-tlse3.fr
+Téo Tinarrage teo.tinarrage@univ-tlse3.fr
diff --git a/README.md b/README.md
index 2cf04f18a0611162f0c19e04fde35b7a26e727ca..95b6d7e63fe2cc81ad34f787a17a12dd8fb43656 100644
--- a/README.md
+++ b/README.md
@@ -1,49 +1,57 @@
 # MOJITO/S
 
 An Open Source System, Energy and Network Monitoring Tools at the O/S level
-
-## Documentation
-
 MojitO/S runs on GNU/Linux
-Usage
-
-```python
-Usage : ./mojitos [-t time] [-f freq] [-r] [-p perf_list] [-l]  \
-                  [-u] [-d network_device] [-o logfile] [-e command arguments...]
-```
 
-Timing
-- If time is 0 then mojitos loops infinitively
-- If -e is present, time and freq are not used
+## Usage
 
-Configuration
-- -r activates RAPL (deprecated)
-- -R activates RAPL
-- -p activates performance counters. perf_list is coma separated list of performance counters without space.
-- -l lists the possible performance counters and quits
-- -d activates network monitoring
-- -u activates system-level load monitoring
-- -s activates statistics of overhead in nanoseconds
+```bash
+Usage : ./bin/mojitos [OPTIONS] [SENSOR ...] [-e <cmd> ...]
 
-MojitO/S is published under the GPL3 license and is part of the [Energumen Project](https://www.irit.fr/energumen/)
+OPTIONS:
+-f|--freq <freq>
+	set amount of measurements per second.
+-t|--time <time>
+	set duration value (seconds). If 0, then loops infinitely.
+-e|--exec <cmd> ...
+	Execute a command with optional arguments.
+	If this option is used, any usage of -t or -f is ignored.
+-o|--logfile <file>
+	specify a log file.
+-s|--overhead-stats
+	enable overhead statistics (nanoseconds).
 
-<img src="https://www.irit.fr/energumen/images/energumen.png" width="100">
+SENSORS:
+-p|--perf-list <perf_list>
+	performance counters
+	perf_list is a coma separated list of performance counters.
+	Ex: instructions,cache_misses
+-l|--list
+	list the available performance counters and quit
+-u|--sysload
+	system load
+-d|--net-dev <net_dev>
+	network monitoring (if network_device is X, tries to detect it automatically)
+-r|--intel-rapl
+	INTEL RAPL
+-c|--cpu-temp
+	processor temperature
+```
 
 ## Installation Instructions
 
-Dependencies
-```bash
-sudo apt install libpowercap0 libpowercap-dev powercap-utils python3
-```
 Download the source code
 ```bash
 git clone https://gitlab.irit.fr/sepia-pub/mojitos.git
 ```
-Compile the code
+The quickest way to compile the code is:
 ```bash
 cd mojitos
+./configure.sh
 make
 ```
+You may want to run `./configure.sh --help` to see configuration options.
+
 To execute mojitos without being root to monitor performance counters
 ```bash
 sudo sh -c 'echo 0 >/proc/sys/kernel/perf_event_paranoid'
@@ -58,43 +66,49 @@ sudo chmod a+w /sys/class/powercap/intel-rapl/*/*/*
 
 RAPL values during 2 seconds with a frequency of 2 Hz
 ```bash
-$ ./mojitos -t 2 -f 2 -r
-#timestamp package-00 core0 dram0 
-1036389.135659868 10986 2869 1526 
-1036389.500183551 1291440 255736 515562 
-1036390.000754048 1333553 228393 689513 
-1036390.500113978 1581967 267944 701536 
+$ ./bin/mojitos -t 2 -f 2 -r
+#timestamp package-00 core0 dram0
+1036389.135659868 10986 2869 1526
+1036389.500183551 1291440 255736 515562
+1036390.000754048 1333553 228393 689513
+1036390.500113978 1581967 267944 701536
 ```
 Performance counters (cpu_cycle, cache_ll_r_a and page_faults) during 4 seconds with a frequency of 1Hz. For cache performance counters, _r and _w are respectively read and write, and _a, _m and _p are respectively access, miss, pending.
 
 ```bash
-$ ./mojitos -t 4 -f 1 -p cpu_cycles,cache_ll_r_a,page_faults
-#timestamp cpu_cycles cache_ll page_faults 
-1036846.351749455 571199 1232 0 
-1036847.001098880 348173344 2451387 872 
-1036848.000166158 388112961 2509305 791 
-1036849.000191883 402255979 2625283 799 
+$ ./bin/mojitos -t 4 -f 1 -p cpu_cycles,cache_ll_r_a,page_faults
+#timestamp cpu_cycles cache_ll page_faults
+1036846.351749455 571199 1232 0
+1036847.001098880 348173344 2451387 872
+1036848.000166158 388112961 2509305 791
+1036849.000191883 402255979 2625283 799
 ```
 
 Network values with no time limit with a frequency of 1Hz. rxp and txp are the number of received and sent packets, while rxb and txp are the number of received and sent bytes.
 ```bash
-$ ./mojitos -t 0 -f 1 -d enp0s25
-#timestamp rxp rxb txp txb 
-1036559.277376027 0 0 0 0 
-1036560.000161101 4 581 2 179 
-1036561.000083968 178 268675 55 4954 
-1036562.000076162 11 1010 5 510 
-1036563.000069724 17 1643 12 3602 
-1036564.000113394 990 1493008 369 27299 
+$ ./bin/mojitos -t 0 -f 1 -d enp0s25
+#timestamp rxp rxb txp txb
+1036559.277376027 0 0 0 0
+1036560.000161101 4 581 2 179
+1036561.000083968 178 268675 55 4954
+1036562.000076162 11 1010 5 510
+1036563.000069724 17 1643 12 3602
+1036564.000113394 990 1493008 369 27299
 ```
 
 Overhead of the monitoring for RAPL and cpu_cycle
 ```bash
-$ ./mojitos -t 5 -f 1 -p cpu_cycles -r -s
-#timestamp cpu_cycles package-00 core0 dram0 overhead 
-1036988.197227391 162214 19898 4944 1586 149612 
-1036989.000151326 332613664 2513116 379577 1115171 739573 
-1036990.000116433 482150700 3321341 587218 1380673 315719 
-1036991.000182835 525984292 3592582 691221 1385982 272182 
-1036992.000165117 397678789 2770561 444030 1375729 510379 
+$ ./bin/mojitos -t 5 -f 1 -p cpu_cycles -r -s
+#timestamp cpu_cycles package-00 core0 dram0 overhead
+1036988.197227391 162214 19898 4944 1586 149612
+1036989.000151326 332613664 2513116 379577 1115171 739573
+1036990.000116433 482150700 3321341 587218 1380673 315719
+1036991.000182835 525984292 3592582 691221 1385982 272182
+1036992.000165117 397678789 2770561 444030 1375729 510379
 ```
+
+## License
+
+MojitO/S is published under the GPL3 license and is part of the [Energumen Project](https://www.irit.fr/energumen/)
+
+<img src="https://www.irit.fr/energumen/images/energumen.png" width="100">
diff --git a/configure.sh b/configure.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9264caa013798c780bf086ed905c6412b71d6174
--- /dev/null
+++ b/configure.sh
@@ -0,0 +1,174 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: GPL-3.0-or-later
+# Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+try() { "$@" || die "cannot $*"; }
+die() { yell "$*"; exit 111; }
+yell() { echo "$0: $*" >&2; }
+echo() { printf '%s\n' "$*"; }
+isnum() {
+	case "${1#[+-]}" in
+	*[!0-9]*|'') return 1 ;;
+	*) return 0 ;;
+	esac
+}
+dprint() {
+	for v in "$@"; do
+		decho "$v : $(eval "echo \$$v")"
+	done
+}
+decho() {
+	[ "$debug" = '1' ] && echo "$@"
+}
+
+debug=0
+target_hdr=src/sensors.h
+target_mk=sensors.mk
+
+nonsensor='counters_option|optparse|sensors|util|info_reader'
+
+hdr_blacklist=$nonsensor
+hdr_whitelist=''
+
+usage() {
+	printf -- 'Usage: %s [-l] [-e <sensor>] [-i <sensor>] [-u <sensor>]\n' "$(basename "$0")" >&2
+	printf -- '-e | --exclude      :   exclude sensor, can be called multiple times\n' >&2
+	printf -- '-i | --include      :   include sensor, can be called multiple times\n' >&2
+	printf -- '-l | --list-sensors :   list all sensors and exit\n' >&2
+	printf -- '-u | --unique       :   only include the specified sensor\n' >&2
+	printf -- '                        if this option is used, any usage of `-e` or `-i` will be ignored\n' >&2
+	exit 1
+}
+
+ls_sensors() {
+	try cd src
+
+	[ -z "$hdr_whitelist" ] && hdr_whitelist='.*'
+	dprint hdr_blacklist >&2
+	dprint hdr_whitelist >&2
+
+	ls -1 *.h |
+		grep -xEv "($hdr_blacklist)\.h" |
+		grep -xE  "($hdr_whitelist)\.h" |
+		sed 's/\.h$//'
+}
+
+# gen_sensors_h(sensor, nb_sensors)
+gen_sensors_h() {
+	sensors=$1
+	nb_sensors=$2
+	nb_sensor_opts=$(
+		for sensor in $sensors; do
+			sed -n 's/.*'"${sensor}"'_opt\[\([0-9]\+\)\].*/\1/p' "src/${sensor}.h"
+		done |
+			paste -s -d '+' |
+			bc
+	)
+
+	dprint sensors >&2
+	dprint nb_sensor_opts >&2
+	isnum "$nb_sensor_opts" || die "could not get total number of sensors's command-line options"
+
+	# gen includes
+	for sensor in $sensors; do
+		printf '#include "%s.h"\n' "$sensor"
+	done
+	printf '\n'
+
+	printf '#define NB_SENSOR %d\n' "$nb_sensors"
+	printf '#define NB_SENSOR_OPT %d\n' "$nb_sensor_opts"
+	printf '\n'
+
+	# gen `init_sensors()`
+	printf 'void init_sensors(Optparse *opts, Sensor *sensors, size_t len, size_t offset, int *nb_defined)\n{\n'
+	printf '    int opt_idx = offset;\n'
+	for sensor in $sensors; do
+		cat <<-!
+		    for (int i = 0; i < ${sensor}.nb_opt; i++) {
+		        opts[opt_idx++] = ${sensor}_opt[i];
+		    }
+		    sensors[(*nb_defined)++] = ${sensor};
+		!
+	done
+	printf '    assert((offset + *nb_defined) <= len);\n'
+	printf '}\n'
+}
+
+gen_sensors_mk() {
+	sensors=$1
+	printf 'CAPTOR_OBJ = '
+	for sensor in $sensors; do
+		printf '$(OBJ_DIR)/%s.o ' "$sensor"
+	done
+	printf '\n'
+}
+
+detect_caps() {
+	[ -r /usr/include/linux/perf_event.h ] && hdr_whitelist=counters
+	[ -d /sys/class/infiniband ] && hdr_whitelist="${hdr_whitelist}|infiniband"
+	[ -r /proc/stat ] && hdr_whitelist="${hdr_whitelist}|load"
+
+	if [ -r /proc/net/route ]; then
+		dev=$(awk 'NR == 2 { print $1 }' /proc/net/route)
+		[ -e "/sys/class/net/$dev" ] && hdr_whitelist="${hdr_whitelist}|network"
+	fi
+
+	vendor=$(awk '/vendor_id/ {print $3; exit}' /proc/cpuinfo)
+	vendor_lc=$(echo "$vendor" | tr 'A-Z' 'a-z')
+	case $vendor_lc in
+	*intel*)
+		hdr_whitelist="${hdr_whitelist}|rapl"
+		;;
+	*)
+		yell "unsupported processor vendor id: $vendor"
+		;;
+	esac
+
+	[ $(ls -1 /sys/class/hwmon | wc -l) -gt 0 ] && hdr_whitelist="${hdr_whitelist}|temperature"
+}
+
+detect_caps
+
+while [ "$1" ]; do
+	case $1 in
+	--include|-i)
+		shift; [ "$1" ] || usage
+		hdr_whitelist="${hdr_whitelist}|${1}"
+		;;
+	--exclude|-e)
+		shift; [ "$1" ] || usage
+		hdr_blacklist="${hdr_blacklist}|${1}"
+		;;
+	--list-sensors|-l)
+		ls_sensors
+		exit 0
+		;;
+	--unique|-u)
+		shift; [ "$1" ] || usage
+		hdr_whitelist=$1
+		;;
+	--help|-h)
+		usage
+		;;
+	esac
+	shift
+done
+
+sensors=$(ls_sensors)
+nb_sensors=$(echo "$sensors" | sed '/^$/d' | wc -l)
+
+if [ "$nb_sensors" -eq 0 ]; then
+	printf -- '0 sensors are selected. cannot build.\n' >&2
+	exit 1
+fi
+
+try gen_sensors_h "$sensors" "$nb_sensors" > "$target_hdr"
+try gen_sensors_mk "$sensors" > "$target_mk"
+
+printf -- 'Run `make` to build `bin/mojitos`.\n' >&2
+printf -- 'The resulting binary will have the %d following sensors:\n' "$nb_sensors" >&2
+echo "$sensors" >&2
+
+make clean >/dev/null
+
diff --git a/counters_group.c b/counters_group.c
deleted file mode 100644
index bc9b448f818df565565416077a9d5788cfbadf62..0000000000000000000000000000000000000000
--- a/counters_group.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-#include <linux/perf_event.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <asm/unistd.h>
-
-#include "counters.h"
-
-struct _counter_t {
-  int nbcores;
-  int nbperf;
-  int *counters;
-};
- 
-static long
-perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
-		int cpu, int group_fd, unsigned long flags) {
-  long res = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
-  if (res == -1) {
-    perror("perf_event_open");
-    fprintf(stderr, "Error opening leader %llx\n", hw_event->config);
-    exit(EXIT_FAILURE);
-  }
-  return res;
-}
-
-counter_t init_counters(const int nb_perf, const __u32 *types, const __u64 *names) {
-  struct perf_event_attr pe;
-  struct _counter_t *counters = malloc(sizeof(struct _counter_t));
-  
-  unsigned int nbcores = sysconf(_SC_NPROCESSORS_ONLN);
-  counters->nbcores=nbcores;
-  counters->nbperf=nb_perf;
-  memset(&pe, 0, sizeof(struct perf_event_attr));
-  pe.size = sizeof(struct perf_event_attr);
-  pe.disabled = 1;
-
-  pe.read_format = PERF_FORMAT_GROUP;
-  counters->counters = malloc((nbcores+1)*sizeof(int));
-
-  for (int core=0; core<nbcores; core++) {
-    counters->counters[core]=-1;
-    for(int idperf=0; idperf<nb_perf; idperf ++){
-      pe.type = types[idperf];
-      pe.config = names[idperf];
-      int res=perf_event_open(&pe, -1, core, counters->counters[core], PERF_FLAG_FD_CLOEXEC);
-      if(counters->counters[core]==-1)
-	counters->counters[core]=res;
-    }
-  }
-  return counters;
-}
-
-void clean_counters(counter_t counters) {
-  for(int core=0; core<counters->nbcores; core++)
-    close(counters->counters[core]);
-  free(counters->counters);
-  free(counters);
-}
-
-void start_counters(counter_t counters) {
-  for(int core=0; core<counters->nbcores; core++)
-    ioctl(counters->counters[core], PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
-}
-void reset_counters(counter_t counters) {
-  for(int core=0; core<counters->nbcores; core++)
-    ioctl(counters->counters[core], PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
-}
-
-struct read_format {
-  uint64_t nr;
-  struct {
-    uint64_t value;
-  } values[];
-};
-
-void get_counters(counter_t counters, long long *values) {
-  int nb_perf = counters->nbperf;
-  size_t buffer_size = sizeof(uint64_t)*(1+nb_perf);
-  struct read_format* buffer = NULL;
-  if(buffer==NULL)
-    buffer = malloc(buffer_size);
-  
-  memset(values, 0, nb_perf*sizeof(long long));
-
-  for (int core=0; core<counters->nbcores; core++) {
-    if (-1 == read(counters->counters[core], buffer, buffer_size)) {
-      perror("PB Lecture resultat");
-      exit(EXIT_FAILURE);
-    } 
-    for(int idperf=0; idperf<=nb_perf; idperf++) {
-      values[idperf] += buffer->values[idperf].value;
-    }
-  }
-  reset_counters(counters);
-}
diff --git a/counters_individual.c b/counters_individual.c
deleted file mode 100644
index 1437aa38442230ae0196dbb14e63c13a81c7b6d9..0000000000000000000000000000000000000000
--- a/counters_individual.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-#include <linux/perf_event.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <asm/unistd.h>
-#include <stdint.h>
-
-struct _counter_t {
-  int nbcores;
-  int nbperf;
-  int **counters;
-  uint64_t *counters_values;
-  uint64_t *tmp_counters_values;
-
-  int* perf_indexes;
-  
-};
-typedef struct _counter_t* counter_t;
-
-#include "counters_option.h"
-
-void show_all_counters() {
-  for(int i=0; i<nb_counter_option;i++)
-    printf("%s\n", perf_static_info[i].name);
-}
-
-void perf_type_key(__u32 **perf_type, __u64 **perf_key, int *indexes, int nb){
-  *perf_type = malloc(nb*sizeof(__u32));
-  *perf_key  = malloc(nb*sizeof(__u64));
-  for(int i=0; i<nb; i++) {
-    (*perf_key)[i]  = perf_static_info[indexes[i]].perf_key;
-    (*perf_type)[i] = perf_static_info[indexes[i]].perf_type;
-  }
-}
-void perf_event_list(char *perf_string, int *nb_perf, int **perf_indexes) {
-  char *token;
-  *nb_perf=0;
-  *perf_indexes=NULL;
-  while((token=strtok(perf_string, ",")) != NULL) {
-    perf_string = NULL;
-    int i;
-    for(i=0; i<nb_counter_option; i++) {
-      if(strcmp(perf_static_info[i].name, token) == 0) {
-	(*nb_perf)++;
-	(*perf_indexes) = realloc(*perf_indexes, sizeof(int)*(*nb_perf));
-	(*perf_indexes)[*nb_perf-1]=i;
-	break;
-      }
-    }
-    if(i == nb_counter_option) {
-      fprintf(stderr, "Unknown performance counter: %s\n", token);
-      exit(EXIT_FAILURE);
-    }
-  }
-}
-
-static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
-			    int cpu, int group_fd, unsigned long flags) {
-  long res = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
-  if (res == -1) {
-    perror("perf_event_open");
-    fprintf(stderr, "Error opening leader %llx\n", hw_event->config);
-    exit(EXIT_FAILURE);
-  }
-  return res;
-}
-
-counter_t _init_counters(const int nb_perf, const __u32 *types, const __u64 *names) {
-  struct perf_event_attr pe;
-  unsigned int nbcores = sysconf(_SC_NPROCESSORS_ONLN);
-  memset(&pe, 0, sizeof(struct perf_event_attr));
-  pe.size = sizeof(struct perf_event_attr);
-  pe.disabled = 1;
-
-  counter_t counters = malloc(sizeof(struct _counter_t));
-  counters->nbperf = nb_perf;
-  counters->nbcores=nbcores;
-  counters->counters=malloc(nb_perf*sizeof(int*));
-  for (int i=0; i<nb_perf; i++) {
-    pe.type = types[i];
-    pe.config = names[i];
-    counters->counters[i] = malloc(nbcores*sizeof(int));
-
-    for (int core=0; core<nbcores; core++) {
-      counters->counters[i][core] = perf_event_open(&pe, -1, core, -1, PERF_FLAG_FD_CLOEXEC);
-    }
-  }
-  return counters;
-}
-
-void clean_counters(void *ptr) {
-  counter_t counters = (counter_t) ptr;
-  for(int counter=0; counter<counters->nbperf; counter++) {
-    for(int core=0; core<counters->nbcores; core++)
-      close(counters->counters[counter][core]);
-    free(counters->counters[counter]);
-  }
-  free(counters->counters);
-  free(counters->counters_values);
-  free(counters->tmp_counters_values);
-  free(counters->perf_indexes);
-
-  free(counters);
-}
-
-void start_counters(counter_t counters) {
-  for(int counter=0; counter<counters->nbperf; counter++)
-    for(int core=0; core<counters->nbcores; core++)
-      ioctl(counters->counters[counter][core], PERF_EVENT_IOC_ENABLE, 0);
-}
-
-void reset_counters(counter_t counters) {
-  for(int counter=0; counter<counters->nbperf; counter++)
-    for(int core=0; core<counters->nbcores; core++)
-      ioctl(counters->counters[counter][core], PERF_EVENT_IOC_RESET, 0);
-}
-
-void _get_counters(counter_t counters, uint64_t *values) {
-  for(int i=0; i<counters->nbperf; i++) {
-    uint64_t accu=0;
-    uint64_t count=0;
-    for (int core=0; core<counters->nbcores; core++) {
-      if (-1 == read(counters->counters[i][core], &count, sizeof(uint64_t))) {
-	fprintf(stderr, "Cannot read result");
-	exit(EXIT_FAILURE);
-      }
-      accu += count;
-    }
-    values[i] = accu;
-  }
-}
-
-
-
-
-
-
-unsigned int init_counters(char* args, void **state) {
-  int nb_perf;
-  int* perf_indexes=NULL;
-
-  perf_event_list(args, &nb_perf, &perf_indexes);
-
-  __u32* perf_type;
-  __u64* perf_key;
-  perf_type_key(&perf_type, &perf_key, perf_indexes, nb_perf);
-  counter_t fd = _init_counters(nb_perf, perf_type, perf_key);
-  free(perf_type);
-  free(perf_key);
-
-  fd->perf_indexes = perf_indexes;
-  fd->counters_values = malloc(nb_perf*sizeof(uint64_t));
-  fd->tmp_counters_values = malloc(nb_perf*sizeof(uint64_t));
-  start_counters(fd);
-  _get_counters(fd, fd->counters_values);
-  *state = (void*) fd;
-
-  return nb_perf;
-}
-
-unsigned int get_counters(uint64_t* results, void*ptr) {
-  counter_t state = (counter_t) ptr;
-
-_get_counters(state, state->tmp_counters_values);
-  for(int i=0; i<state->nbperf; i++)
-    results[i] = state->tmp_counters_values[i] - state->counters_values[i];
-
-  memcpy(state->counters_values, state->tmp_counters_values, state->nbperf * sizeof(uint64_t));
-  return state->nbperf;
-}
-
-void label_counters(char **labels, void*ptr) {
-  counter_t state = (counter_t) ptr;
-  for(int i=0; i<state->nbperf;i++)
-    labels[i] = perf_static_info[state->perf_indexes[i]].name;
-
-}
diff --git a/counters_option.py b/counters_option.py
deleted file mode 100755
index 877a33afbd61814acf3ddbb4c5311bd043c4ef37..0000000000000000000000000000000000000000
--- a/counters_option.py
+++ /dev/null
@@ -1,62 +0,0 @@
-#! /usr/bin/python3
-
-# SPDX-License-Identifier: GPL-3.0-or-later
-# Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr>
-
-linux_include = '/usr/include/linux/perf_event.h'
-
-string = """#include <linux/perf_event.h>
-
-typedef struct counter_option {
-  char *name;
-  __u32 perf_type;
-  __u64 perf_key;
-} counter_option;
-
-static counter_option perf_static_info[] = {"""
-print(string)
-
-nb = 0
-
-black_list = ['stalled_cycles_frontend','stalled_cycles_backend',
-              'cache_l1i', 'cache_op_write', 'cache_result_miss']
-
-with open(linux_include, 'r') as infile:
-    mode = ''
-    for line in infile:
-        if 'perf_hw_id' in line:
-            mode = 'PERF_TYPE_HARDWARE'
-        elif 'perf_hw_cache_' in line:
-            mode = 'PERF_TYPE_HW_CACHE'
-        elif 'perf_sw_id' in line:
-            mode = 'PERF_TYPE_SOFTWARE'
-        elif 'PERF_COUNT_' in line and '=' in line:
-            perf_name = line.split()[0]
-            short_perf = perf_name[14:].lower()
-            if short_perf in black_list:
-                continue
-            if mode == 'PERF_TYPE_HW_CACHE':
-                for op_id, op_id_str in enumerate(['r', 'w', 'p']):
-                    op_id_names = ['PERF_COUNT_HW_CACHE_OP_READ', 'PERF_COUNT_HW_CACHE_OP_WRITE', 'PERF_COUNT_HW_CACHE_OP_PREFETCH']
-                    for result_id, result_id_str in enumerate(['a', 'm']):
-                        result_id_names = ['PERF_COUNT_HW_CACHE_RESULT_ACCESS', 'PERF_COUNT_HW_CACHE_RESULT_MISS']
-
-                        res = '{ .name = "%s_%s_%s", .perf_type = %s, .perf_key = %s | (%s >> 8) | (%s >> 16) },' % (
-                            short_perf, op_id_str, result_id_str,
-                            mode,
-                            perf_name,
-                            op_id_names[op_id],
-                            result_id_names[result_id])
-                                                                                     
-                        print(res)
-                        nb += 1
-
-            else:
-                res = '{ .name = "'+short_perf+'", .perf_type = '+mode+', .perf_key = '+perf_name+'},'
-                print(res)
-                nb += 1
-
-
-print('};')
-
-print('static unsigned int nb_counter_option =',nb,';')
diff --git a/doc/mojitos.pre.1 b/doc/mojitos.pre.1
new file mode 100644
index 0000000000000000000000000000000000000000..af9cb9bc5681422a8af870eead9cea78e88b0504
--- /dev/null
+++ b/doc/mojitos.pre.1
@@ -0,0 +1,81 @@
+.Dd January 9, 2023
+.Dt MOJITOS 1
+.Os
+.Sh NAME
+.Nm mojitos
+.Nd An open source system monitoring tool.
+.Sh SYNOPSIS
+.Nm mojitos
+.Op Ar OPTIONS
+.Op Ar SENSOR ...
+.Op Fl e Ar cmd ...
+.Sh DESCRIPTION
+.Nm
+is a monitoring tool with a multitude of sensors that does measurements at the OS level.
+.Nm
+runs on GNU/Linux.
+USAGE
+.Sh EXIT STATUS
+.Ex
+.Sh EXAMPLES
+RAPL values during 2 seconds with a frequency of 2Hz:
+.Bd -literal -offset indent
+$ mojitos -t 2 -f 2 -r
+#timestamp package-00 core0 dram0
+1036389.135659868 10986 2869 1526
+1036389.500183551 1291440 255736 515562
+1036390.000754048 1333553 228393 689513
+1036390.500113978 1581967 267944 701536
+.Ed
+.Pp
+Performance counters (cpu_cycle, cache_ll_r_a and page_faults) during 4 seconds
+with a frequency of 1Hz.
+In the context of cache performance counters (as with "cache_ll_r_a"),
+ _r and _w suffixes mean respectively read and write,
+while the _a, _m and _p ones mean respectively access, miss and pending.
+.Pp
+.Bd -literal -offset indent
+$ ./mojitos -t 4 -f 1 -p cpu_cycles,cache_ll_r_a,page_faults
+#timestamp cpu_cycles cache_ll page_faults
+1036846.351749455 571199 1232 0
+1036847.001098880 348173344 2451387 872
+1036848.000166158 388112961 2509305 791
+1036849.000191883 402255979 2625283 799
+.Ed
+.Pp
+Network values with no time limit with a frequency of 1Hz.
+rxp and txp are the number of received and sent packets, while rxb and txp are
+the number of received and sent bytes.
+.Pp
+.Bd -literal -offset indent
+$ ./mojitos -t 0 -f 1 -d enp0s25
+#timestamp rxp rxb txp txb
+1036559.277376027 0 0 0 0
+1036560.000161101 4 581 2 179
+1036561.000083968 178 268675 55 4954
+1036562.000076162 11 1010 5 510
+1036563.000069724 17 1643 12 3602
+1036564.000113394 990 1493008 369 27299
+.Ed
+.Pp
+overhead of the monitoring when using RAPL and the cpu_cycle performance counter:
+.Pp
+.Bd -literal -offset indent
+$ ./mojitos -t 5 -f 1 -p cpu_cycles -r -s
+#timestamp cpu_cycles package-00 core0 dram0 overhead
+1036988.197227391 162214 19898 4944 1586 149612
+1036989.000151326 332613664 2513116 379577 1115171 739573
+1036990.000116433 482150700 3321341 587218 1380673 315719
+1036991.000182835 525984292 3592582 691221 1385982 272182
+1036992.000165117 397678789 2770561 444030 1375729 510379
+.Ed
+.Sh SEE ALSO
+.Xr perf 1 ,
+.Xr lscpu 1 ,
+.Xr sysfs 5 ,
+.Xr proc 5
+.Sh LICENSE
+MojitO/S is published under the GPL3 license and is part of the
+.Lk https://www.irit.fr/energumen/ [Energumen Project]
+.Sh BUGS
+Yes.
diff --git a/doc/sensor_ex.c b/doc/sensor_ex.c
new file mode 100644
index 0000000000000000000000000000000000000000..5fc8eeb559a618e3a30b0354dd94860e2a2b0c21
--- /dev/null
+++ b/doc/sensor_ex.c
@@ -0,0 +1,124 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+/*
+ * compilation:
+ *
+
+# normally, this part is done with `configure.sh`, but here we need to
+# do this manually
+cat <<EOF > src/sensors.h
+#include "sensor_ex.h"
+
+#define NB_SENSOR 1
+#define NB_SENSOR_OPT 1
+
+void init_sensors(Optparse *opts, Sensor *sensors, size_t len, size_t offset, int *nb_defined)
+{
+    int opt_idx = offset;
+    for (int i = 0; i < sensor_ex.nb_opt; i++) {
+        opts[opt_idx++] = sensor_ex_opt[i];
+    }
+    sensors[(*nb_defined)++] = sensor_ex;
+    assert((offset + *nb_defined) <= len);
+}
+EOF
+
+# idem
+echo 'CAPTOR_OBJ = doc/sensor_ex.o' > sensors.mk
+
+# actual compilation here
+gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og -c doc/sensor_ex.c -o obj/sensor_ex.o
+gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og -c doc/util.c -o obj/util.o
+gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og -c doc/mojitos.c -o obj/mojitos.o
+gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og obj/util.o obj/mojitos.o obj/sensor_ex.o -o bin/mojitos
+
+ *
+**/
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+#define NB_SENSOR 3
+
+struct Accumulator {
+    int v[NB_SENSOR];
+};
+typedef struct Accumulator Accumulator;
+
+void _get_acc(int v[NB_SENSOR])
+{
+    for (int i = 0; i < NB_SENSOR; i++) {
+        v[i]++;
+    }
+}
+
+unsigned int init_acc(char *none, void **ptr)
+{
+    /* "none" refers to an optionnal command-line argument */
+    /* there is none in this case, so this parameter is not used */
+    UNUSED(none);
+
+    Accumulator *state = malloc(sizeof(Accumulator));
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        state->v[i] = -1;
+    }
+
+    *ptr = (void *)state;
+    _get_acc(state->v);
+
+    return NB_SENSOR;
+}
+
+unsigned int get_acc(uint64_t *results, void *ptr)
+{
+    Accumulator *state = (Accumulator *)ptr;
+
+    _get_acc(state->v);
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        results[i] = state->v[i];
+    }
+
+    return NB_SENSOR;
+}
+
+char *_labels_accumulator[NB_SENSOR] = {"acc1", "acc2", "acc3"};
+void label_acc(char **labels, void *none)
+{
+    UNUSED(none);
+    for (int i = 0; i < NB_SENSOR; i++) {
+        labels[i] = _labels_accumulator[i];
+    }
+}
+
+void clean_acc(void *ptr)
+{
+    Accumulator *state = (Accumulator *)ptr;
+
+    if (state == NULL) {
+        return;
+    }
+
+    free(state);
+}
diff --git a/doc/sensor_ex.h b/doc/sensor_ex.h
new file mode 100644
index 0000000000000000000000000000000000000000..9fd95d3e0f32b0013ca8115edb36f82a9203da69
--- /dev/null
+++ b/doc/sensor_ex.h
@@ -0,0 +1,46 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+/*
+ * Example of a basic sensor: an accumulator
+**/
+
+unsigned int init_acc(char *, void **);
+unsigned int get_acc(uint64_t *results, void *);
+void clean_acc(void *);
+void label_acc(char **labels, void *);
+
+Sensor sensor_ex = {
+    .init = init_acc,
+    .get = get_acc,
+    .clean = clean_acc,
+    .label = label_acc,
+    .nb_opt = 1,
+};
+
+Optparse sensor_ex_opt[1] = {
+    {
+        .longname = "accumulator",
+        .shortname = 'a',
+        .argtype = OPTPARSE_NONE,		/* OPTPARSE_NONE / OPTPARSE_OPTIONAL / OPTPARSE_REQUIRED */
+        .usage_arg = NULL,
+        .usage_msg = "dumb accumulator",
+    },
+};
diff --git a/frapl.c b/frapl.c
deleted file mode 100644
index 433a28c13ac5ecd847c95935fb3fa0e8f7131935..0000000000000000000000000000000000000000
--- a/frapl.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*******************************************************
- Copyright (C) 2022-2023 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <inttypes.h>
-
-
-#define MAX_HEADER 128
-
-char *get_frapl_string(const char *filename) {
-  int fd = open(filename, O_RDONLY);
-  if( fd == -1)
-    return NULL;
-  char *result = malloc(MAX_HEADER);
-  int nb = read(fd, result, MAX_HEADER);
-  close(fd);
-  result[nb-1] = 0;
-  return (result);
-}  
-
-void test_append(char* name, int i) {
-  //char last = name[strlen(name)-1];
-  //if (last>='0' && last <= '9')
-  //  return;
-  sprintf(name+strlen(name), "%d", i);
-}
-
-
-struct _frapl_t {
-  unsigned int nb;
-  char **names;
-  int *fids;
-  uint64_t* values;
-  uint64_t* tmp_values;
-
-};
-typedef struct _frapl_t _frapl_t;
-
-
-void add_frapl_source(_frapl_t* rapl, char*name, char*energy_uj) {
-  rapl->nb += 1;
-  rapl->names = realloc(rapl->names, sizeof(char**)*rapl->nb);
-  rapl->fids = realloc(rapl->fids, sizeof(int*)*rapl->nb);
-  
-  rapl->names[rapl->nb-1] = malloc(strlen(name)+1);
-  strcpy(rapl->names[rapl->nb-1], name);
-  //printf("%s\n", energy_uj);
-
-  rapl->fids[rapl->nb-1] = open(energy_uj, O_RDONLY);
-}
-
-
-void _get_frapl(uint64_t *values, _frapl_t* rapl) {
-  static char buffer[512];
-
-  for (int i = 0; i < rapl->nb; i++) {
-
-    pread(rapl->fids[i], buffer, 100, 0);
-    values[i] = strtoull(buffer, NULL, 10);
-  }
-}
-
-
-unsigned int init_frapl(char* none, void **ptr) {
-  _frapl_t *rapl = malloc(sizeof(_frapl_t));
-  rapl->nb = 0;
-  rapl->names = NULL;
-  rapl->fids = NULL;
-
-  char buffer[1024];
-  char *name_base = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:%d/%s";
-  char *name_sub = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:%d/intel-rapl:%d:%d/%s";
-
-  for (int i=0;; i++) {
-    sprintf(buffer, name_base, i, "name");
-    char *tmp = get_frapl_string(buffer);
-    if (tmp == NULL) break;
-    //printf("%s\n", tmp);
-    test_append(tmp, i);
-    //printf("%s -> %s\n", buffer, tmp);
-
-    sprintf(buffer, name_base, i, "energy_uj");
-    add_frapl_source(rapl, tmp, buffer);
-    free(tmp);
-
-    for (int j=0;; j++) {
-      sprintf(buffer, name_sub, i, i, j, "name");
-      char *tmp_sub = get_frapl_string(buffer);
-      if (tmp_sub == NULL) break;
-      //printf("%s\n", tmp_sub);
-      test_append(tmp_sub, i);
-      //printf("%s -> %s\n", buffer, tmp_sub);
-
-
-      sprintf(buffer, name_sub, i, i, j, "energy_uj");
-      add_frapl_source(rapl, tmp_sub, buffer);
-      
-      free(tmp_sub);
-    }
-  }
-
-  rapl->values = calloc(sizeof(uint64_t), rapl->nb);
-  rapl->tmp_values = calloc(sizeof(uint64_t), rapl->nb);
-
-  _get_frapl(rapl->values, rapl);
-
-  *ptr = (void*)rapl;
-  return rapl->nb;
-}
-
-
-unsigned int get_frapl(uint64_t* results, void* ptr) {
-  _frapl_t* state = (_frapl_t*) ptr;
-  _get_frapl(state->tmp_values, state);
-  for(int i=0; i<state->nb; i++)
-    results[i] = state->tmp_values[i] - state->values[i];
-
-  memcpy(state->values, state->tmp_values, sizeof(uint64_t)*state->nb);
-  return state->nb;
-}
-
-void clean_frapl(void *ptr) {
-  _frapl_t* rapl = (_frapl_t*) ptr;
-  for(int i=0; i<rapl->nb; i++) {
-    free(rapl->names[i]);
-    close(rapl->fids[i]);
-  }
-  free(rapl->names);
-  free(rapl->fids);
-  free(rapl->values);
-  free(rapl->tmp_values);
-  free(rapl);
-}
-
-
-void label_frapl(char **labels, void *ptr) {
-  _frapl_t* rapl = (_frapl_t*) ptr;
-  for(int i=0; i<rapl->nb; i++)
-    labels[i] = rapl->names[i];
-}
diff --git a/infiniband.c b/infiniband.c
deleted file mode 100644
index 49dca077f6f8552b5707a75c4ad5bc2ffa2da462..0000000000000000000000000000000000000000
--- a/infiniband.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-#include <stdlib.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <glob.h>
-
-#include <stdint.h>
-
-struct network_t {
-  uint64_t values[4];
-  uint64_t tmp_values[4];
-  int sources[4];
-};
-unsigned int _get_network(uint64_t* results, int* sources);
-
-
-unsigned int init_infiniband(char* infi_path, void**ptr) {
-  if(infi_path==NULL)
-    return 0;
-
-  if(strcmp(infi_path,"X")==0) {
-
-    glob_t res;
-  
-    glob("/sys/class/infiniband/*/ports/*/counters/", 0, NULL, &res);
-    if(res.gl_pathc == 0)
-      return 0;
-    infi_path = res.gl_pathv[0];
-  }
-  
-  char *filenames[] = {"%s/port_rcv_packets",
-		       "%s/port_rcv_data",
-		       "%s/port_xmit_packets",
-		       "%s/port_xmit_data"};
-
-  struct network_t *state = malloc(sizeof(struct network_t));
-
-  char buffer[1024];
-  for(int i=0; i<4; i++) {
-    sprintf(buffer, filenames[i], infi_path);
-    state->sources[i] = open(buffer, O_RDONLY);
-  }
-  
-  *ptr = (void*) state;
-  _get_network(state->values, state->sources);
-
-  return 4;
-}
-
-char *_labels_infiniband[4] = {"irxp", "irxb", "itxp", "itxb"};
-void label_infiniband(char **labels, void*none) {
-  for(int i=0; i<4; i++)
-    labels[i] = _labels_infiniband[i];
-}
diff --git a/infiniband.h b/infiniband.h
deleted file mode 100644
index af8b2b6d0993a138dee43e4aa7aa8cd71fad661d..0000000000000000000000000000000000000000
--- a/infiniband.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-unsigned int init_infiniband(char* infi_path, void**ptr);
-void label_infiniband(char **labels, void*);
diff --git a/lib/optparse.h b/lib/optparse.h
new file mode 100644
index 0000000000000000000000000000000000000000..ee7dcb6c967fc158df36b4a844a13d5f7b6746ac
--- /dev/null
+++ b/lib/optparse.h
@@ -0,0 +1,392 @@
+/* Optparse --- portable, reentrant, embeddable, getopt-like option parser
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * To get the implementation, define OPTPARSE_IMPLEMENTATION.
+ * Optionally define OPTPARSE_API to control the API's visibility
+ * and/or linkage (static, __attribute__, __declspec).
+ *
+ * The POSIX getopt() option parser has three fatal flaws. These flaws
+ * are solved by Optparse.
+ *
+ * 1) Parser state is stored entirely in global variables, some of
+ * which are static and inaccessible. This means only one thread can
+ * use getopt(). It also means it's not possible to recursively parse
+ * nested sub-arguments while in the middle of argument parsing.
+ * Optparse fixes this by storing all state on a local struct.
+ *
+ * 2) The POSIX standard provides no way to properly reset the parser.
+ * This means for portable code that getopt() is only good for one
+ * run, over one argv with one option string. It also means subcommand
+ * options cannot be processed with getopt(). Most implementations
+ * provide a method to reset the parser, but it's not portable.
+ * Optparse provides an optparse_arg() function for stepping over
+ * subcommands and continuing parsing of options with another option
+ * string. The Optparse struct itself can be passed around to
+ * subcommand handlers for additional subcommand option parsing. A
+ * full reset can be achieved by with an additional optparse_init().
+ *
+ * 3) Error messages are printed to stderr. This can be disabled with
+ * opterr, but the messages themselves are still inaccessible.
+ * Optparse solves this by writing an error message in its errmsg
+ * field. The downside to Optparse is that this error message will
+ * always be in English rather than the current locale.
+ *
+ * Optparse should be familiar with anyone accustomed to getopt(), and
+ * it could be a nearly drop-in replacement. The option string is the
+ * same and the fields have the same names as the getopt() global
+ * variables (optarg, optind, optopt).
+ *
+ * Optparse also supports GNU-style long options with optparse_long().
+ * The interface is slightly different and simpler than getopt_long().
+ *
+ * By default, argv is permuted as it is parsed, moving non-option
+ * arguments to the end. This can be disabled by setting the `permute`
+ * field to 0 after initialization.
+ */
+#ifndef OPTPARSE_H
+#define OPTPARSE_H
+
+#ifndef OPTPARSE_API
+#  define OPTPARSE_API
+#endif
+
+struct optparse {
+    char **argv;
+    int permute;
+    int optind;
+    int optopt;
+    char *optarg;
+    char errmsg[64];
+    int subopt;
+};
+
+enum optparse_argtype {
+    OPTPARSE_NONE,
+    OPTPARSE_REQUIRED,
+    OPTPARSE_OPTIONAL
+};
+
+struct optparse_long {
+    const char *longname;
+    int shortname;
+    enum optparse_argtype argtype;
+    char *usage_arg;
+    char *usage_msg;
+    void *(*fn)(void *, size_t);	/* SUBJECT TO CHANGE */
+};
+
+/**
+ * Initializes the parser state.
+ */
+OPTPARSE_API
+void optparse_init(struct optparse *options, char **argv);
+
+/**
+ * Read the next option in the argv array.
+ * @param optstring a getopt()-formatted option string.
+ * @return the next option character, -1 for done, or '?' for error
+ *
+ * Just like getopt(), a character followed by no colons means no
+ * argument. One colon means the option has a required argument. Two
+ * colons means the option takes an optional argument.
+ */
+OPTPARSE_API
+int optparse(struct optparse *options, const char *optstring);
+
+/**
+ * Handles GNU-style long options in addition to getopt() options.
+ * This works a lot like GNU's getopt_long(). The last option in
+ * longopts must be all zeros, marking the end of the array. The
+ * longindex argument may be NULL.
+ */
+OPTPARSE_API
+int optparse_long(struct optparse *options,
+                  const struct optparse_long *longopts,
+                  int *longindex);
+
+/**
+ * Used for stepping over non-option arguments.
+ * @return the next non-option argument, or NULL for no more arguments
+ *
+ * Argument parsing can continue with optparse() after using this
+ * function. That would be used to parse the options for the
+ * subcommand returned by optparse_arg(). This function allows you to
+ * ignore the value of optind.
+ */
+OPTPARSE_API
+char *optparse_arg(struct optparse *options);
+
+/* Implementation */
+#ifdef OPTPARSE_IMPLEMENTATION
+
+#define OPTPARSE_MSG_INVALID "invalid option"
+#define OPTPARSE_MSG_MISSING "option requires an argument"
+#define OPTPARSE_MSG_TOOMANY "option takes no arguments"
+
+static int optparse_error(struct optparse *options, const char *msg, const char *data)
+{
+    unsigned p = 0;
+    const char *sep = " -- '";
+    while (*msg)
+        options->errmsg[p++] = *msg++;
+    while (*sep)
+        options->errmsg[p++] = *sep++;
+    while (p < sizeof(options->errmsg) - 2 && *data)
+        options->errmsg[p++] = *data++;
+    options->errmsg[p++] = '\'';
+    options->errmsg[p++] = '\0';
+    return '?';
+}
+
+OPTPARSE_API
+void optparse_init(struct optparse *options, char **argv)
+{
+    options->argv = argv;
+    options->permute = 1;
+    options->optind = argv[0] != 0;
+    options->subopt = 0;
+    options->optarg = 0;
+    options->errmsg[0] = '\0';
+}
+
+static int optparse_is_dashdash(const char *arg)
+{
+    return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
+}
+
+static int optparse_is_shortopt(const char *arg)
+{
+    return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
+}
+
+static int optparse_is_longopt(const char *arg)
+{
+    return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
+}
+
+static void optparse_permute(struct optparse *options, int index)
+{
+    char *nonoption = options->argv[index];
+    int i;
+    for (i = index; i < options->optind - 1; i++)
+        options->argv[i] = options->argv[i + 1];
+    options->argv[options->optind - 1] = nonoption;
+}
+
+static int optparse_argtype(const char *optstring, char c)
+{
+    int count = OPTPARSE_NONE;
+    if (c == ':')
+        return -1;
+    for (; *optstring && c != *optstring; optstring++);
+    if (!*optstring)
+        return -1;
+    if (optstring[1] == ':')
+        count += optstring[2] == ':' ? 2 : 1;
+    return count;
+}
+
+OPTPARSE_API
+int optparse(struct optparse *options, const char *optstring)
+{
+    int type;
+    char *next;
+    char *option = options->argv[options->optind];
+    options->errmsg[0] = '\0';
+    options->optopt = 0;
+    options->optarg = 0;
+    if (option == 0) {
+        return -1;
+    } else if (optparse_is_dashdash(option)) {
+        options->optind++; /* consume "--" */
+        return -1;
+    } else if (!optparse_is_shortopt(option)) {
+        if (options->permute) {
+            int index = options->optind++;
+            int r = optparse(options, optstring);
+            optparse_permute(options, index);
+            options->optind--;
+            return r;
+        } else {
+            return -1;
+        }
+    }
+    option += options->subopt + 1;
+    options->optopt = option[0];
+    type = optparse_argtype(optstring, option[0]);
+    next = options->argv[options->optind + 1];
+    switch (type) {
+    case -1: {
+        char str[2] = {0, 0};
+        str[0] = option[0];
+        options->optind++;
+        return optparse_error(options, OPTPARSE_MSG_INVALID, str);
+    }
+    case OPTPARSE_NONE:
+        if (option[1]) {
+            options->subopt++;
+        } else {
+            options->subopt = 0;
+            options->optind++;
+        }
+        return option[0];
+    case OPTPARSE_REQUIRED:
+        options->subopt = 0;
+        options->optind++;
+        if (option[1]) {
+            options->optarg = option + 1;
+        } else if (next != 0) {
+            options->optarg = next;
+            options->optind++;
+        } else {
+            char str[2] = {0, 0};
+            str[0] = option[0];
+            options->optarg = 0;
+            return optparse_error(options, OPTPARSE_MSG_MISSING, str);
+        }
+        return option[0];
+    case OPTPARSE_OPTIONAL:
+        options->subopt = 0;
+        options->optind++;
+        if (option[1])
+            options->optarg = option + 1;
+        else
+            options->optarg = 0;
+        return option[0];
+    }
+    return 0;
+}
+
+OPTPARSE_API
+char *optparse_arg(struct optparse *options)
+{
+    char *option = options->argv[options->optind];
+    options->subopt = 0;
+    if (option != 0)
+        options->optind++;
+    return option;
+}
+
+static int optparse_longopts_end(const struct optparse_long *longopts, int i)
+{
+    return !longopts[i].longname && !longopts[i].shortname;
+}
+
+static void optparse_from_long(const struct optparse_long *longopts, char *optstring)
+{
+    char *p = optstring;
+    int i;
+    for (i = 0; !optparse_longopts_end(longopts, i); i++) {
+        if (longopts[i].shortname && longopts[i].shortname < 127) {
+            int a;
+            *p++ = longopts[i].shortname;
+            for (a = 0; a < (int)longopts[i].argtype; a++)
+                *p++ = ':';
+        }
+    }
+    *p = '\0';
+}
+
+/* Unlike strcmp(), handles options containing "=". */
+static int optparse_longopts_match(const char *longname, const char *option)
+{
+    const char *a = option, *n = longname;
+    if (longname == 0)
+        return 0;
+    for (; *a && *n && *a != '='; a++, n++)
+        if (*a != *n)
+            return 0;
+    return *n == '\0' && (*a == '\0' || *a == '=');
+}
+
+/* Return the part after "=", or NULL. */
+static char *optparse_longopts_arg(char *option)
+{
+    for (; *option && *option != '='; option++);
+    if (*option == '=')
+        return option + 1;
+    else
+        return 0;
+}
+
+static int optparse_long_fallback(struct optparse *options,
+                                  const struct optparse_long *longopts,
+                                  int *longindex)
+{
+    int result;
+    char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
+    optparse_from_long(longopts, optstring);
+    result = optparse(options, optstring);
+    if (longindex != 0) {
+        *longindex = -1;
+        if (result != -1) {
+            int i;
+            for (i = 0; !optparse_longopts_end(longopts, i); i++)
+                if (longopts[i].shortname == options->optopt)
+                    *longindex = i;
+        }
+    }
+    return result;
+}
+
+OPTPARSE_API
+int optparse_long(struct optparse *options,
+                  const struct optparse_long *longopts,
+                  int *longindex)
+{
+    int i;
+    char *option = options->argv[options->optind];
+    if (option == 0) {
+        return -1;
+    } else if (optparse_is_dashdash(option)) {
+        options->optind++; /* consume "--" */
+        return -1;
+    } else if (optparse_is_shortopt(option)) {
+        return optparse_long_fallback(options, longopts, longindex);
+    } else if (!optparse_is_longopt(option)) {
+        if (options->permute) {
+            int index = options->optind++;
+            int r = optparse_long(options, longopts, longindex);
+            optparse_permute(options, index);
+            options->optind--;
+            return r;
+        } else {
+            return -1;
+        }
+    }
+
+    /* Parse as long option. */
+    options->errmsg[0] = '\0';
+    options->optopt = 0;
+    options->optarg = 0;
+    option += 2; /* skip "--" */
+    options->optind++;
+    for (i = 0; !optparse_longopts_end(longopts, i); i++) {
+        const char *name = longopts[i].longname;
+        if (optparse_longopts_match(name, option)) {
+            char *arg;
+            if (longindex)
+                *longindex = i;
+            options->optopt = longopts[i].shortname;
+            arg = optparse_longopts_arg(option);
+            if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
+                return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
+            }
+            if (arg != 0) {
+                options->optarg = arg;
+            } else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
+                options->optarg = options->argv[options->optind];
+                if (options->optarg == 0)
+                    return optparse_error(options, OPTPARSE_MSG_MISSING, name);
+                else
+                    options->optind++;
+            }
+            return options->optopt;
+        }
+    }
+    return optparse_error(options, OPTPARSE_MSG_INVALID, option);
+}
+
+#endif /* OPTPARSE_IMPLEMENTATION */
+#endif /* OPTPARSE_H */
diff --git a/load.c b/load.c
deleted file mode 100644
index b3d154d794c35f96baeb64ae61d447400633cbc0..0000000000000000000000000000000000000000
--- a/load.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/*******************************************************
- Copyright (C) 2019-2021 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-
-#define LOAD_BUFFER_SIZE 1024
-char buffer[LOAD_BUFFER_SIZE];
-
-static int load_fid=-1;
-static uint64_t load_values[10]={0,0,0,0,0,0,0,0,0,0};
-static uint64_t tmp_load_values[10]={0,0,0,0,0,0,0,0,0,0};
-
-void _get_load(uint64_t* results) {
-  pread(load_fid, buffer, LOAD_BUFFER_SIZE-1, 0);
-  int pos=0;
-  while(buffer[pos] > '9' || buffer[pos] < '0') pos++;
-  for(int i=0; i<10; i++) {
-    results[i] = strtoull(buffer+pos, NULL, 10);
-    while(buffer[pos] <= '9' && buffer[pos] >= '0') pos++;
-    pos++;
-  }
-}
-
-// Public interface
-
-unsigned int init_load(char* argument, void **state) {
-  load_fid = open("/proc/stat", O_RDONLY);
-  _get_load(load_values);
-  return 10;
-}
-
-unsigned int get_load(uint64_t* results, void* state) {
-  _get_load(tmp_load_values);
-  for(int i=0; i<10; i++)
-    results[i] = tmp_load_values[i] - load_values[i];
-
-  memcpy(load_values, tmp_load_values, sizeof(load_values));
-  return 10;
-}
-
-void clean_load(void *state) {
-  close(load_fid);
-}
-
-char *_labels[10] = {"user","nice","system","idle","iowait","irq",
-  "softirq","steal","guest","guest_nice"};
-void label_load(char **labels, void*none) {
-  for(int i=0; i<10; i++)
-    labels[i] = _labels[i];
-}
diff --git a/makefile b/makefile
index 433b76f63d171d59b4965f8981f9cfd262d80e11..f0fa1b12d47134962335572e6fd52bc5ca001261 100644
--- a/makefile
+++ b/makefile
@@ -1,30 +1,67 @@
-all: mojitos
+.POSIX:
 
-OBJECTS = mojitos.o counters_individual.o rapl.o frapl.o network.o load.o infiniband.o temperature.o
+SRC_DIR = src
+DOC_DIR = doc
+OBJ_DIR = obj
+BIN_DIR = bin
 
-mojitos:$(OBJECTS)
-	gcc $(DEBUG) -O3 -Wall -o mojitos $(OBJECTS) -lpowercap
+BIN = mojitos
 
-OBJECTS_GRP = $(subst _individual,_group, $(OBJECTS))
-mojitos_group: $(OBJECTS_GRP) counters_option.h
-	gcc $(DEBUG) -O3 -Wall -o mojitos_group $(OBJECTS_GRP) -lpowercap
+CAPTOR_OBJ =
 
-counters_%.o: counters_%.c counters.h counters_option.h
-	gcc $(DEBUG) -c -O3 -Wall $< -o $@
+include ./sensors.mk
 
-counters_option.h: counters_option.py
-	./counters_option.py > counters_option.h
+OBJ =  \
+	$(CAPTOR_OBJ) \
+	$(OBJ_DIR)/util.o
 
+CC = gcc
+CPPFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -Wno-unused-function -I./lib
+CFLAGS = $(CPPFLAGS) -O3 -Werror
+LDFLAGS =
 
-mojitos.o: mojitos.c counters_option.h
-	gcc $(DEBUG) -c -O3 -Wall $< -o $@
+ASTYLE = astyle --style=kr -xf -s4 -k3 -n -Z -Q
 
-debug: DEBUG = -DDEBUG -g
 
-debug: all
+all: $(BIN) man
 
-%.o : %.c %.h
-	gcc $(DEBUG) -c -O3 -Wall $< -o $@
+$(BIN): $(BIN_DIR) $(OBJ) $(OBJ_DIR)/$(BIN).o
+	$(CC) $(LDFLAGS) -o $(BIN_DIR)/$(BIN) $(OBJ) $(OBJ_DIR)/$(BIN).o
+
+$(OBJ): $(OBJ_DIR)
+$(OBJ_DIR)/counters.o: $(SRC_DIR)/counters_option.h
+
+$(OBJ_DIR)/$(BIN).o: $(SRC_DIR)/$(BIN).c $(SRC_DIR)/counters_option.h
+	$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(SRC_DIR)/%.h
+	$(CC) $(CFLAGS) -c $< -o $@
+
+$(SRC_DIR)/counters_option.h: $(SRC_DIR)/counters_option.sh
+	sh ./$(SRC_DIR)/counters_option.sh > $(SRC_DIR)/counters_option.h
+
+$(OBJ_DIR):
+	mkdir -p $(OBJ_DIR)
+
+$(BIN_DIR):
+	mkdir -p $(BIN_DIR)
+
+debug: CFLAGS = $(CPPFLAGS) -DDEBUG -g -Og
+debug: $(BIN)
+
+format:
+	$(ASTYLE) $(SRC_DIR)/*.[ch] \
+		$(DOC_DIR)/*.[ch] \
 
 clean:
-	\rm -f *~ *.o mojitos_group mojitos counters_option.h
+	\rm -f $(OBJ_DIR)/* $(BIN_DIR)/* \
+		$(SRC_DIR)/counters_option.h \
+		$(DOC_DIR)/test_main_ex \
+		$(DOC_DIR)/info_reader_ex
+
+man: $(BIN)
+	awk -v "usage=$$($(BIN_DIR)/$(BIN) -1)" \
+		'/^USAGE/ { $$0=usage } 1' \
+		doc/mojitos.pre.1 > doc/mojitos.1 2>/dev/null
+
+.PHONY: all clean mojitos debug format man
diff --git a/mojitos b/mojitos
deleted file mode 100755
index ab3f7b4824740935e193e4d9efee35d0f20849fa..0000000000000000000000000000000000000000
Binary files a/mojitos and /dev/null differ
diff --git a/mojitos.c b/mojitos.c
deleted file mode 100644
index e80e692f5a4498e02e60761cee1a2d568860c314..0000000000000000000000000000000000000000
--- a/mojitos.c
+++ /dev/null
@@ -1,259 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-#include <signal.h>
-#include <inttypes.h>
-#include <unistd.h>
-
-#include "counters.h"
-#include "rapl.h"
-#include "frapl.h"
-#include "network.h"
-#include "infiniband.h"
-#include "load.h"
-#include "temperature.h"
-
-
-
-void usage(char** argv) {
-  printf("Usage : %s [-t time] [-f freq] [-r] [-p perf_list] [-l] [-u] [-c] [-d network_device] [-i infiniband_path] [-o logfile] [-e command arguments...]\n", argv[0]);
-  printf("if time==0 then loops infinitively\n");
-  printf("if -e is present, time and freq are not used\n");
-  printf("-r activates RAPL\n");
-  printf("-R activates the file version of RAPL\n");
-  printf("-p activates performance counters\n");
-  printf("   perf_list is coma separated list of performance counters without space. Ex: instructions,cache_misses\n");
-  printf("-l lists the possible performance counters and quits\n");
-  printf("-d activates network monitoring (if network_device is X, tries to detect it automatically)\n");
-  printf("-i activates infiniband monitoring (if infiniband_path is X, tries to detect it automatically)\n");
-  printf("-s activates statistics of overhead in nanoseconds\n");
-  printf("-u activates report of system load\n");
-  printf("-c activates report of processor temperature\n");
-  exit(EXIT_SUCCESS);
-}
-
-void sighandler(int none) {
-}
-
-void flush(int none) {
-  exit(0);
-}
-
-FILE *output;
-void flushexit() {
-  fflush(output);
-  fclose(output);
-}
-
-typedef unsigned int (initializer_t)(char*, void **);
-typedef void (labeler_t)(char **, void*);
-typedef unsigned int (*getter_t)(uint64_t*, void*);
-typedef void (*cleaner_t)(void*);
-
-unsigned int nb_sources=0;
-void **states=NULL;
-getter_t *getter=NULL;
-cleaner_t *cleaner=NULL;
-
-unsigned int nb_sensors=0;
-char **labels=NULL;
-uint64_t *values=NULL;
-
-void add_source(initializer_t init, char* arg, labeler_t labeler,
-		getter_t get, cleaner_t clean) {
-    nb_sources++;
-    states = realloc(states, nb_sources*sizeof(void*));
-    int nb = init(arg, &states[nb_sources-1]);
-    if (nb == 0) {
-      nb_sources--;
-      states = realloc(states, nb_sources*sizeof(void*));
-      return;
-    }
-
-    getter = realloc(getter, nb_sources*sizeof(void*));
-    getter[nb_sources-1] = get;
-    cleaner = realloc(cleaner, nb_sources*sizeof(void*));
-    cleaner[nb_sources-1] = clean;
-
-    labels = realloc(labels, (nb_sensors+nb)*sizeof(char*));
-    labeler(labels+nb_sensors, states[nb_sources-1]);
-    
-    values = realloc(values, (nb_sensors+nb)*sizeof(uint64_t));
-    nb_sensors += nb;
-}
-
-int main(int argc, char **argv) {
-  int total_time=1;
-  int delta=0;
-  int frequency=1;
-  char **application = NULL;
-
-  int stat_mode = -1;
-
-  if(argc==1)
-    usage(argv);
-  
-  output=stdout;
-  
-  atexit(flushexit);
-  signal(15, flush);
-  
-  int c;
-  while ((c = getopt (argc, argv, "ilhcftdeoprRsu")) != -1 && application==NULL)
-    switch (c) {
-    case 'f':
-      frequency=atoi(argv[optind]);
-      break;
-    case 't':
-      total_time=atoi(argv[optind]);
-      delta=1;
-      if(total_time==0) {
-	total_time=1;
-	delta=0;
-      }
-      break;
-    case 'd':
-      add_source(init_network, argv[optind], label_network, get_network, clean_network);
-      break;
-    case 'i':
-      add_source(init_infiniband, argv[optind], label_infiniband, get_network, clean_network);
-      break;
-    case 'o':
-      output = fopen(argv[optind],"wb");
-      break;
-    case 'e':
-      application=&argv[optind];
-      signal(17,sighandler);
-      break;
-    case 'p':
-      add_source(init_counters, argv[optind], label_counters, get_counters, clean_counters);
-      break;
-    case 'r':
-      add_source(init_rapl, NULL, label_rapl, get_rapl, clean_rapl);
-      break;
-    case 'R':
-      add_source(init_frapl, NULL, label_frapl, get_frapl, clean_frapl);
-      break;
-    case 'u':
-      add_source(init_load, NULL, label_load, get_load, clean_load);
-      break;
-    case 'c':
-      add_source(init_temperature, NULL, label_temperature, get_temperature, clean_temperature);
-      break;
-    case 's':
-      stat_mode=0;
-      break;
-    case 'l':
-      show_all_counters();
-      exit(EXIT_SUCCESS);
-    default:
-      usage(argv);
-    }
-
-
-  setvbuf(output, NULL, _IONBF, BUFSIZ);
-  struct timespec ts;
-  struct timespec ts_ref;
-
-  fprintf(output, "#timestamp ");
-
-  for(int i=0; i<nb_sensors; i++)
-    fprintf(output, "%s ", labels[i]);
-
-  if(stat_mode==0)
-    fprintf(output, "overhead ");
-
-  fprintf(output, "\n");
-
-  unsigned long int stat_data=0;
-
-  for (int temps = 0; temps <total_time*frequency; temps+=delta) {
-    clock_gettime(CLOCK_MONOTONIC, &ts_ref);
-
-    // Get Data
-    unsigned int current = 0;
-    for(int i=0; i<nb_sources; i++)
-      current += getter[i](&values[current], states[i]);
-    
-    if(application != NULL) {
-
-      if(fork()==0){
-	execvp(application[0], application);
-	exit(0);
-      }
-      pause();
-      clock_gettime(CLOCK_MONOTONIC, &ts);
-      if(ts.tv_nsec >= ts_ref.tv_nsec)
-	fprintf(output, "%ld.%09ld ", (ts.tv_sec-ts_ref.tv_sec), ts.tv_nsec-ts_ref.tv_nsec);
-      else
-	fprintf(output, "%ld.%09ld ", (ts.tv_sec-ts_ref.tv_sec)-1,1000000000+ts.tv_nsec-ts_ref.tv_nsec);
-    }
-    else {
-#ifdef DEBUG
-      clock_gettime(CLOCK_MONOTONIC, &ts);
-      fprintf(stderr, "%ld\n", (ts.tv_nsec-ts_ref.tv_nsec)/1000);
-      //Indiv: mean: 148 std: 31 % med: 141 std: 28 %
-      //Group: mean: 309 std: 41 % med: 297 std: 39 %
-#endif
-      if(stat_mode==0) {
-	clock_gettime(CLOCK_MONOTONIC, &ts);
-      if(ts.tv_nsec >= ts_ref.tv_nsec)
-	stat_data = ts.tv_nsec-ts_ref.tv_nsec;
-      else
-	stat_data = 1000000000+ts.tv_nsec-ts_ref.tv_nsec;
-    }
-    
-      // Treat Data
-      fprintf(output, "%ld.%09ld ", ts_ref.tv_sec, ts_ref.tv_nsec);
-    }
-    
-    for(int i=0; i<nb_sensors; i++)
-      fprintf(output, "%" PRIu64 " ", values[i]);
-
-    if(stat_mode==0)
-      fprintf(output, "%ld ", stat_data);
-
-    fprintf(output, "\n");
-
-    if(application != NULL)
-      break;
-
-    clock_gettime(CLOCK_MONOTONIC, &ts);
-    usleep(1000*1000/frequency-(ts.tv_nsec/1000)%(1000*1000/frequency));
-  }
-
-  for(int i=0; i<nb_sources;i++)
-    cleaner[i](states[i]);
-
-  if(nb_sources > 0) {
-    free(getter);
-    free(cleaner);
-    free(labels);
-    free(values);
-    free(states);
-  }
-}
-
-
-
-
diff --git a/network.c b/network.c
deleted file mode 100644
index 6e3f494bd49b86c68e452efd202873821b9c6832..0000000000000000000000000000000000000000
--- a/network.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-
-struct network_t {
-  uint64_t values[4];
-  uint64_t tmp_values[4];
-  int sources[4];
-};
-
-unsigned int _get_network(uint64_t* results, int* sources) {
-  if(sources==NULL)
-    return 0;
-  char buffer[128];
-  for(int i=0; i<4; i++){
-    pread(sources[i], buffer, 127, 0);
-
-    results[i] = strtoull(buffer, NULL, 10);
-  }
-  return 4;
-}
-
-
-
-unsigned int init_network(char* dev, void**ptr) {
-  if(dev==NULL)
-    return 0;
-
-  if(strcmp(dev,"X")==0) {
-    int f = open("/proc/net/route", O_RDONLY);
-    char buffer[1000];
-    read(f, buffer, 999);
-    char *start_of_dev = index(buffer, '\n')+1;
-    char *end_of_dev = index(start_of_dev, '\t');
-    *end_of_dev='\0';
-    dev = start_of_dev;
-    close(f);
-  }
-  
-  char *filenames[] = {"/sys/class/net/%s/statistics/rx_packets",
-		       "/sys/class/net/%s/statistics/rx_bytes",
-		       "/sys/class/net/%s/statistics/tx_packets",
-		       "/sys/class/net/%s/statistics/tx_bytes"};
-
-  struct network_t *state = malloc(sizeof(struct network_t));
-
-  char buffer2[256];
-  for(int i=0; i<4; i++) {
-    sprintf(buffer2, filenames[i], dev);
-    state->sources[i] = open(buffer2, O_RDONLY);
-  }
-  *ptr = (void*) state;
-  _get_network(state->values, state->sources);
-
-  return 4;
-}
-
-unsigned int get_network(uint64_t* results, void* ptr) {
-  struct network_t *state = (struct network_t *) ptr;
-  _get_network(state->tmp_values, state->sources);
-  for(int i=0; i<4; i++)
-    results[i] = state->tmp_values[i] - state->values[i];
-
-  memcpy(state->values, state->tmp_values, 4*sizeof(uint64_t));
-  return 4;
-}
-
-void clean_network(void *ptr) {
-  struct network_t *state = (struct network_t *) ptr;
-  if(state==NULL)
-    return;
-  for(int i=0;i<4;i++)
-    close(state->sources[i]);
-  free(state);
-}    
-
-char *_labels_network[4] = {"rxp", "rxb", "txp", "txb"};
-void label_network(char **labels, void*none) {
-  for(int i=0; i<4; i++)
-    labels[i] = _labels_network[i];
-}
diff --git a/rapl.c b/rapl.c
deleted file mode 100644
index 14b4a58ce302d1251680540155fb372d98acec46..0000000000000000000000000000000000000000
--- a/rapl.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <powercap/powercap-rapl.h>
-
-struct _rapl_t {
-  powercap_rapl_pkg* pkgs;
-  uint32_t nb_pkgs;
-  uint32_t nb;
-  char **names;
-  uint32_t* zones;
-  uint32_t* packages;
-  uint64_t* values;
-  uint64_t* tmp_values;
-};
-
-typedef struct _rapl_t _rapl_t;
-
-
-
-
-
-
-const int nb_zones = 3;
-const int rapl_zones[3] = { POWERCAP_RAPL_ZONE_PACKAGE,   POWERCAP_RAPL_ZONE_CORE,   POWERCAP_RAPL_ZONE_DRAM};
-
-
-#define MAX_LEN_NAME 100
-
-// values [zone + package *nbzones] microjoules
-void _get_rapl(uint64_t *values, _rapl_t* rapl) {
-  for (int i = 0; i < rapl->nb; i++) {
-#ifdef DEBUG
-    int ret =
-#endif
-      powercap_rapl_get_energy_uj(&rapl->pkgs[rapl->packages[i]],
-				rapl->zones[i],
-				&values[i]);
-#ifdef DEBUG
-    printf("GETRAPL: package %d, zone %d, name %s, ret: %d\n", rapl->packages[i], rapl->zones[i], rapl->names[i], ret);
-#endif
-  }
-}
-
-unsigned int init_rapl(char* none, void **ptr) {
-  // get number of processor sockets
-  _rapl_t* rapl= malloc(sizeof(struct _rapl_t));
-  rapl->nb = 0;
-  rapl->packages = NULL;
-  rapl->zones = NULL;
-
-  rapl->nb_pkgs = powercap_rapl_get_num_instances();
-  //rapl->nb_pkgs = powercap_rapl_get_num_packages();
-  
-  if (rapl->nb_pkgs == 0) {
-    perror("no packages found (maybe the kernel module isn't loaded?)");
-    exit(-1);
-  }
-  rapl->pkgs = malloc(rapl->nb_pkgs * sizeof(powercap_rapl_pkg));  
-  for (int package = 0; package < rapl->nb_pkgs; package++)
-    if (powercap_rapl_init(package, &rapl->pkgs[package], 0)) {
-      perror("powercap_rapl_init, check access (root needed ?)");
-      exit(-1);
-    }
-
-  rapl->names = NULL;
-    
-  char _name[MAX_LEN_NAME+1];
-  char _name2[MAX_LEN_NAME+11];
-
-  for (unsigned int package = 0; package < rapl->nb_pkgs; package++) {
-    for(unsigned int zone=0; zone < nb_zones; zone++) {
-      int length=powercap_rapl_get_name(&rapl->pkgs[package], rapl_zones[zone],
-			     _name, MAX_LEN_NAME);
-      if (length>0) {
-
-	sprintf(_name2, "%s%u", _name, package);
-
-	rapl->nb++;
-	rapl->names = realloc(rapl->names, sizeof(char*)*rapl->nb);
-	rapl->names[rapl->nb-1] = malloc(sizeof(char) * (strlen(_name2)+1));
-	rapl->zones = realloc(rapl->zones, sizeof(uint32_t)*rapl->nb);
-	rapl->packages = realloc(rapl->packages, sizeof(uint32_t)*rapl->nb);
-	
-	strcpy(rapl->names[rapl->nb-1], _name2);
-	rapl->zones[rapl->nb-1] = rapl_zones[zone];
-	rapl->packages[rapl->nb-1] = package;
-      }
-#ifdef DEBUG
-      printf("%d %d %d %d %s\n\n", length, package, zone, rapl_zones[zone], _name2);
-#endif
-    }
-  }
-#ifdef DEBUG
-  printf("Result of init\n");
-  for(int i=0; i<rapl->nb; i++)
-    printf("package %d, zone %d, name %s\n", rapl->packages[i], rapl->zones[i], rapl->names[i]);
-#endif
-
-  rapl->values = calloc(sizeof(uint64_t), rapl->nb);
-  rapl->tmp_values = calloc(sizeof(uint64_t), rapl->nb);
-
-  _get_rapl(rapl->values, rapl);
-
-  *ptr = (void*)rapl;
-  return rapl->nb;
-}
-
-
-
-unsigned int get_rapl(uint64_t* results, void* ptr) {
-  _rapl_t* state = (_rapl_t*) ptr;
-  _get_rapl(state->tmp_values, state);
-  for(int i=0; i<state->nb; i++)
-    results[i] = state->tmp_values[i] - state->values[i];
-
-  memcpy(state->values, state->tmp_values, sizeof(uint64_t)*state->nb);
-  return state->nb;
-}
-
-
-
-
-void clean_rapl(void* ptr) {
-  _rapl_t* rapl = (_rapl_t*) ptr;
-  for (int package = 0; package < rapl->nb_pkgs; package++)
-    if (powercap_rapl_destroy(&rapl->pkgs[package]))
-      perror("powercap_rapl_destroy");
-  for (int elem=0; elem<rapl->nb; elem++) 
-      free(rapl->names[elem]);
-  
-  free(rapl->names);
-  free(rapl->pkgs);
-  free(rapl->zones);
-  free(rapl->packages);
-  free(rapl->values);
-  free(rapl->tmp_values);
-  free(rapl);
-}
-
-void label_rapl(char **labels, void *ptr) {
-  _rapl_t* rapl = (_rapl_t*) ptr;
-  for(int i=0; i<rapl->nb; i++)
-    labels[i] = rapl->names[i];
-}
diff --git a/src/counters.c b/src/counters.c
new file mode 100644
index 0000000000000000000000000000000000000000..8013ff863757b2f614094cd96dc09e12776467c8
--- /dev/null
+++ b/src/counters.c
@@ -0,0 +1,240 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+#include <linux/perf_event.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <asm/unistd.h>
+#include <stdint.h>
+#include "util.h"
+
+
+struct Counter {
+    int nbcores;
+    int nbperf;
+    int **counters;
+    uint64_t *counters_values;
+    uint64_t *tmp_counters_values;
+
+    int *perf_indexes;
+
+};
+typedef struct Counter Counter;
+
+#include "counters_option.h"
+
+void *show_all_counters(void *none1, size_t none2)
+{
+    for (unsigned int i = 0; i < nb_counter_option; i++) {
+        printf("%s\n", perf_static_info[i].name);
+    }
+    UNUSED(none1);
+    UNUSED(none2);
+    exit(EXIT_SUCCESS);
+    return NULL; /* not reached */
+}
+
+void perf_type_key(__u32 **perf_type, __u64 **perf_key, int *indexes, int nb)
+{
+    *perf_type = malloc(nb * sizeof(__u32));
+    *perf_key  = malloc(nb * sizeof(__u64));
+
+    for (int i = 0; i < nb; i++) {
+        (*perf_key)[i]  = perf_static_info[indexes[i]].perf_key;
+        (*perf_type)[i] = perf_static_info[indexes[i]].perf_type;
+    }
+}
+void perf_event_list(char *perf_string, int *nb_perf, int **perf_indexes)
+{
+    char *token;
+    *nb_perf = 0;
+    *perf_indexes = NULL;
+
+    while ((token = strtok(perf_string, ",")) != NULL) {
+        perf_string = NULL;
+        unsigned int i;
+
+        for (i = 0; i < nb_counter_option; i++) {
+            if (strcmp(perf_static_info[i].name, token) == 0) {
+                (*nb_perf)++;
+                (*perf_indexes) = realloc(*perf_indexes, sizeof(int) * (*nb_perf));
+                (*perf_indexes)[*nb_perf - 1] = i;
+                break;
+            }
+        }
+
+        if (i == nb_counter_option) {
+            fprintf(stderr, "Unknown performance counter: %s\n", token);
+            exit(EXIT_FAILURE);
+        }
+    }
+}
+
+static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
+                            int cpu, int group_fd, unsigned long flags)
+{
+    long res = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
+
+    if (res == -1) {
+        perror("perf_event_open");
+        fprintf(stderr, "Error opening leader %llx\n", hw_event->config);
+        exit(EXIT_FAILURE);
+    }
+
+    return res;
+}
+
+Counter *_init_counters(const int nb_perf, const __u32 *types, const __u64 *names)
+{
+    struct perf_event_attr pe;
+    unsigned int nbcores = sysconf(_SC_NPROCESSORS_ONLN);
+    memset(&pe, 0, sizeof(struct perf_event_attr));
+    pe.size = sizeof(struct perf_event_attr);
+    pe.disabled = 1;
+
+    Counter *counters = malloc(sizeof(struct Counter));
+    counters->nbperf = nb_perf;
+    counters->nbcores = nbcores;
+    counters->counters = malloc(nb_perf * sizeof(int *));
+
+    for (int i = 0; i < nb_perf; i++) {
+        pe.type = types[i];
+        pe.config = names[i];
+        counters->counters[i] = malloc(nbcores * sizeof(int));
+
+        for (unsigned int core = 0; core < nbcores; core++) {
+            counters->counters[i][core] = perf_event_open(&pe, -1, core, -1, PERF_FLAG_FD_CLOEXEC);
+        }
+    }
+
+    return counters;
+}
+
+void clean_counters(void *ptr)
+{
+    Counter *counters = (Counter *) ptr;
+
+    for (int counter = 0; counter < counters->nbperf; counter++) {
+        for (int core = 0; core < counters->nbcores; core++) {
+            close(counters->counters[counter][core]);
+        }
+
+        free(counters->counters[counter]);
+    }
+
+    free(counters->counters);
+    free(counters->counters_values);
+    free(counters->tmp_counters_values);
+    free(counters->perf_indexes);
+
+    free(counters);
+}
+
+void start_counters(Counter *counters)
+{
+    for (int counter = 0; counter < counters->nbperf; counter++) {
+        for (int core = 0; core < counters->nbcores; core++) {
+            ioctl(counters->counters[counter][core], PERF_EVENT_IOC_ENABLE, 0);
+        }
+    }
+}
+
+void reset_counters(Counter *counters)
+{
+    for (int counter = 0; counter < counters->nbperf; counter++) {
+        for (int core = 0; core < counters->nbcores; core++) {
+            ioctl(counters->counters[counter][core], PERF_EVENT_IOC_RESET, 0);
+        }
+    }
+}
+
+void _get_counters(Counter *counters, uint64_t *values)
+{
+    for (int i = 0; i < counters->nbperf; i++) {
+        uint64_t accu = 0;
+        uint64_t count = 0;
+
+        for (int core = 0; core < counters->nbcores; core++) {
+            if (-1 == read(counters->counters[i][core], &count, sizeof(uint64_t))) {
+                fprintf(stderr, "Cannot read result");
+                exit(EXIT_FAILURE);
+            }
+            accu += count;
+        }
+
+        values[i] = accu;
+    }
+}
+
+
+
+
+
+
+unsigned int init_counters(char *args, void **state)
+{
+    int nb_perf;
+    int *perf_indexes = NULL;
+
+    perf_event_list(args, &nb_perf, &perf_indexes);
+
+    __u32 *perf_type;
+    __u64 *perf_key;
+    perf_type_key(&perf_type, &perf_key, perf_indexes, nb_perf);
+    Counter *fd = _init_counters(nb_perf, perf_type, perf_key);
+    free(perf_type);
+    free(perf_key);
+
+    fd->perf_indexes = perf_indexes;
+    fd->counters_values = malloc(nb_perf * sizeof(uint64_t));
+    fd->tmp_counters_values = malloc(nb_perf * sizeof(uint64_t));
+    start_counters(fd);
+    _get_counters(fd, fd->counters_values);
+    *state = (void *) fd;
+
+    return nb_perf;
+}
+
+unsigned int get_counters(uint64_t *results, void *ptr)
+{
+    Counter *state = (Counter *) ptr;
+
+    _get_counters(state, state->tmp_counters_values);
+
+    for (int i = 0; i < state->nbperf; i++) {
+        results[i] = modulo_substraction(state->tmp_counters_values[i], state->counters_values[i]);
+    }
+
+    memcpy(state->counters_values, state->tmp_counters_values, state->nbperf * sizeof(uint64_t));
+    return state->nbperf;
+}
+
+void label_counters(char **labels, void *ptr)
+{
+    Counter *state = (Counter *) ptr;
+
+    for (int i = 0; i < state->nbperf; i++) {
+        labels[i] = perf_static_info[state->perf_indexes[i]].name;
+    }
+}
diff --git a/src/counters.h b/src/counters.h
new file mode 100644
index 0000000000000000000000000000000000000000..4374cd1f6ea0fb4e59d1226b03753d38a2fa3b2b
--- /dev/null
+++ b/src/counters.h
@@ -0,0 +1,54 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+unsigned int init_counters(char *, void **);
+unsigned int get_counters(uint64_t *results, void *);
+void clean_counters(void *);
+void label_counters(char **labels, void *);
+void *show_all_counters(void *, size_t);
+
+Sensor counters = {
+    .init = init_counters,
+    .get = get_counters,
+    .clean = clean_counters,
+    .label = label_counters,
+    .nb_opt = 2,
+};
+
+Optparse counters_opt[2] = {
+    {
+        .longname = "perf-list",
+        .shortname = 'p',
+        .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<perf_list>",
+        .usage_msg = "performance counters\n"
+        "\tperf_list is a coma separated list of performance counters.\n"
+        "\tEx: instructions,cache_misses",
+    },
+    {
+        .longname = "list",
+        .shortname = 'l',
+        .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "list the available performance counters and quit",
+        .fn = show_all_counters,
+    },
+};
+
diff --git a/src/counters_option.sh b/src/counters_option.sh
new file mode 100644
index 0000000000000000000000000000000000000000..76f92757f1e09207c1113421c76cd6a67d22f3df
--- /dev/null
+++ b/src/counters_option.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: GPL-3.0-or-later
+# Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+linux_include=/usr/include/linux/perf_event.h
+
+echo '
+typedef struct CounterOption{
+    char *name;
+    __u32 perf_type;
+    __u64 perf_key;
+} CounterOption;
+
+static CounterOption perf_static_info[] = {'
+
+nb=0
+
+while IFS= read line; do
+	case $line in
+	*perf_hw_id*)
+        mode=PERF_TYPE_HARDWARE
+		;;
+    *perf_hw_cache_*)
+        mode=PERF_TYPE_HW_CACHE
+		;;
+    *perf_sw_id*)
+        mode=PERF_TYPE_SOFTWARE
+		;;
+    *PERF_COUNT_*=*)
+		perf_name=$(echo "$line" | awk '{print $1}')
+		short_perf=$(echo "$perf_name" | sed 's/PERF_COUNT_[HS]W_//' | tr 'A-Z' 'a-z')
+		case $short_perf in
+		# blacklist
+		stalled_cycles_frontend|stalled_cycles_backend|cache_l1i|cache_op_write|cache_result_miss)
+			continue
+			;;
+		esac
+
+		if [ "$mode" != 'PERF_TYPE_HW_CACHE' ]; then
+			printf '    { .name = "%s", .perf_type = %s, .perf_key = %s},\n' \
+					"$short_perf" \
+					"$mode" \
+					"$perf_name"
+
+            : $((nb += 1))
+			continue
+		fi
+
+		# $mode == PERF_TYPE_HW_CACHE
+        for op_id in \
+        	'r PERF_COUNT_HW_CACHE_OP_READ' \
+        	'w PERF_COUNT_HW_CACHE_OP_WRITE' \
+        	'p PERF_COUNT_HW_CACHE_OP_PREFETCH'
+        do
+            op_id_str=${op_id% *}
+            op_id_name=${op_id#* }
+
+            for result_id in \
+            	'a PERF_COUNT_HW_CACHE_RESULT_ACCESS' \
+            	'm PERF_COUNT_HW_CACHE_RESULT_MISS'
+            do
+                result_id_str=${result_id% *}
+                result_id_name=${result_id#* }
+
+            	printf '    {'
+            	printf ' .name = "%s_%s_%s",' \
+            		"$short_perf" \
+            		"$op_id_str" \
+            		"$result_id_str"
+            	printf ' .perf_type = %s,' \
+            		"$mode"
+            	printf ' .perf_key = %s | (%s >> 8) | (%s >> 16)' \
+            		"$perf_name" \
+            		"$op_id_name" \
+            		"$result_id_name"
+            	printf ' },\n'
+                : $((nb += 1))
+            done
+        done
+		;;
+	esac
+done < "$linux_include"
+
+echo '};'
+
+printf '\nstatic unsigned int nb_counter_option = %d;\n' "$nb"
+
diff --git a/src/infiniband.c b/src/infiniband.c
new file mode 100644
index 0000000000000000000000000000000000000000..aa4a8de532fc61e162a1a2069815e262c3ec2b5b
--- /dev/null
+++ b/src/infiniband.c
@@ -0,0 +1,87 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <glob.h>
+#include <stdint.h>
+#include "util.h"
+
+#define NB_SENSOR 4
+
+struct Network {
+    uint64_t values[NB_SENSOR];
+    uint64_t tmp_values[NB_SENSOR];
+    int sources[NB_SENSOR];
+};
+typedef struct Network Network;
+
+unsigned int _get_network(uint64_t *results, int *sources);
+
+
+unsigned int init_infiniband(char *infi_path, void **ptr)
+{
+    if (infi_path == NULL) {
+        return 0;
+    }
+
+    if (strcmp(infi_path, "X") == 0) {
+
+        glob_t res;
+
+        glob("/sys/class/infiniband/*/ports/*/counters/", 0, NULL, &res);
+
+        if (res.gl_pathc == 0) {
+            return 0;
+        }
+
+        infi_path = res.gl_pathv[0];
+    }
+
+    char *filenames[] = {"%s/port_rcv_packets",
+                         "%s/port_rcv_data",
+                         "%s/port_xmit_packets",
+                         "%s/port_xmit_data"
+                        };
+
+    Network *state = malloc(sizeof(Network));
+
+    char buffer[1024];
+    for (int i = 0; i < NB_SENSOR; i++) {
+        snprintf(buffer, 1024, filenames[i], infi_path);
+        state->sources[i] = open(buffer, O_RDONLY);
+    }
+
+    *ptr = (void *) state;
+    _get_network(state->values, state->sources);
+
+    return NB_SENSOR;
+}
+
+char *_labels_infiniband[NB_SENSOR] = {"irxp", "irxb", "itxp", "itxb"};
+void label_infiniband(char **labels, void *none)
+{
+    UNUSED(none);
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        labels[i] = _labels_infiniband[i];
+    }
+}
diff --git a/counters.h b/src/infiniband.h
similarity index 56%
rename from counters.h
rename to src/infiniband.h
index 751557ab5b308c0eda13c61f077d5bdff6519cd1..e30d78bee90f936056e8847a071e9fe817c9bea1 100644
--- a/counters.h
+++ b/src/infiniband.h
@@ -1,5 +1,5 @@
 /*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
 
     This file is part of Mojitos.
 
@@ -18,9 +18,24 @@
 
  *******************************************************/
 
-unsigned int init_counters(char*, void **);
-unsigned int get_counters(uint64_t* results, void*);
-void clean_counters(void *);
-void label_counters(char **labels, void*);
+unsigned int init_infiniband(char *infi_path, void **ptr);
+void label_infiniband(char **labels, void *);
+
+Sensor infiniband = {
+    .init = init_infiniband,
+    .get = NULL,
+    .clean = NULL,
+    .label = label_infiniband,
+    .nb_opt = 1,
+};
+
+Optparse infiniband_opt[1] = {
+    {
+        .longname = "monitor-infiniband",
+        .shortname = 'i',
+        .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<infiniband_path>",
+        .usage_msg = "infiniband monitoring (if infiniband_path is X, tries to detect it automatically)",
+    },
+};
 
-void show_all_counters();
diff --git a/src/load.c b/src/load.c
new file mode 100644
index 0000000000000000000000000000000000000000..64502e587c1472e603d96d36f53a3068d67053e9
--- /dev/null
+++ b/src/load.c
@@ -0,0 +1,108 @@
+/*******************************************************
+ Copyright (C) 2019-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include "util.h"
+
+#define LOAD_BUFFER_SIZE 1024
+#define LOAD_VALUES_SIZE 10
+char buffer[LOAD_BUFFER_SIZE];
+
+static int load_fid = -1;
+static uint64_t load_values[LOAD_VALUES_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static uint64_t tmp_load_values[LOAD_VALUES_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static char *stat = "/proc/stat";
+
+void _get_load(uint64_t *results)
+{
+    if (pread(load_fid, buffer, LOAD_BUFFER_SIZE - 1, 0) < 0) {
+        perror("pread");
+        exit(1);
+    }
+
+    int pos = 0;
+
+    while (buffer[pos] > '9' || buffer[pos] < '0') {
+        pos++;
+    }
+
+    for (int i = 0; i < LOAD_VALUES_SIZE; i++) {
+        results[i] = strtoull(buffer + pos, NULL, LOAD_VALUES_SIZE);
+
+        while (buffer[pos] <= '9' && buffer[pos] >= '0') {
+            pos++;
+        }
+
+        pos++;
+    }
+}
+
+// Public interface
+
+unsigned int init_load(char *argument, void **state)
+{
+    UNUSED(argument);
+    UNUSED(state);
+    load_fid = open(stat, O_RDONLY);
+    if (load_fid < 0) {
+        fprintf(stderr, "%s ", stat);
+        perror("open");
+        exit(1);
+    }
+
+    _get_load(load_values);
+    return LOAD_VALUES_SIZE;
+}
+
+unsigned int get_load(uint64_t *results, void *state)
+{
+    UNUSED(state);
+    _get_load(tmp_load_values);
+
+    for (int i = 0; i < LOAD_VALUES_SIZE; i++) {
+        results[i] = tmp_load_values[i] - load_values[i];
+    }
+
+    memcpy(load_values, tmp_load_values, sizeof(load_values));
+    return LOAD_VALUES_SIZE;
+}
+
+void clean_load(void *state)
+{
+    UNUSED(state);
+    close(load_fid);
+}
+
+char *_labels[LOAD_VALUES_SIZE] = {
+    "user", "nice", "system", "idle", "iowait", "irq",
+    "softirq", "steal", "guest", "guest_nice"
+};
+void label_load(char **labels, void *none)
+{
+    UNUSED(none);
+
+    for (int i = 0; i < LOAD_VALUES_SIZE; i++) {
+        labels[i] = _labels[i];
+    }
+}
diff --git a/load.h b/src/load.h
similarity index 61%
rename from load.h
rename to src/load.h
index 42ce968062dcb68a13ebf67713eabc865abe1d7a..6d8d29804f93c6be0805b6670486210f74452fdf 100644
--- a/load.h
+++ b/src/load.h
@@ -1,5 +1,5 @@
 /*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
 
     This file is part of Mojitos.
 
@@ -18,7 +18,25 @@
 
  *******************************************************/
 
-unsigned int init_load(char*, void **);
-unsigned int get_load(uint64_t* results, void*);
+unsigned int init_load(char *, void **);
+unsigned int get_load(uint64_t *results, void *);
 void clean_load(void *);
-void label_load(char **labels, void*);
+void label_load(char **labels, void *);
+
+Sensor load = {
+    .init = init_load,
+    .get = get_load,
+    .clean = clean_load,
+    .label = label_load,
+    .nb_opt = 1,
+};
+
+Optparse load_opt[1] = {
+    {
+        .longname = "sysload",
+        .shortname = 'u',
+        .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "system load",
+    },
+};
diff --git a/src/mojitos.c b/src/mojitos.c
new file mode 100644
index 0000000000000000000000000000000000000000..4274b6a114805b6f07fd5c52cae73d8255c2c702
--- /dev/null
+++ b/src/mojitos.c
@@ -0,0 +1,383 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include "util.h"
+
+#define OPTPARSE_IMPLEMENTATION
+#define OPTPARSE_API static
+#include "optparse.h"
+
+typedef unsigned int (*initializer_t)(char *, void **);
+typedef void (*labeler_t)(char **, void *);
+typedef unsigned int (*getter_t)(uint64_t *, void *);
+typedef void (*cleaner_t)(void *);
+
+typedef struct Opt Opt;
+typedef struct Sensor Sensor;
+/* optparse typedef */
+typedef struct optparse_long Optparse;
+
+struct Sensor {
+    initializer_t init;
+    getter_t get;
+    cleaner_t clean;
+    labeler_t label;
+    int nb_opt;
+};
+
+int nb_defined_sensors = 0;
+
+#include "sensors.h"
+
+Sensor sensors[NB_SENSOR];
+
+#define NB_OPT 5
+Optparse opts[NB_OPT + NB_SENSOR_OPT + 1] = {
+    {
+        .longname = "freq", .shortname = 'f', .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<freq>",
+        .usage_msg = "set amount of measurements per second.",
+    },
+    {
+        .longname = "time", .shortname = 't', .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<time>",
+        .usage_msg = "set duration value (seconds). If 0, then loops infinitely.",
+    },
+    {
+        .longname = "exec", .shortname = 'e', .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<cmd> ...",
+        .usage_msg = "Execute a command with optional arguments.\n"
+        "\tIf this option is used, any usage of -t or -f is ignored.",
+    },
+    {
+        .longname = "logfile", .shortname = 'o', .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<file>",
+        .usage_msg = "specify a log file.",
+    },
+    {
+        .longname = "overhead-stats", .shortname = 's', .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "enable overhead statistics (nanoseconds).",
+    },
+};
+
+void dumpopt(Optparse *opt)
+{
+    printf(".It Fl %c | Fl \\-%s", opt->shortname, opt->longname);
+    if (opt->usage_arg != NULL) {
+        printf(" Ar %s", opt->usage_arg);
+    }
+    printf("\n");
+    printf("%s\n", opt->usage_msg);
+}
+
+void dumpopts(Optparse *opts, size_t nb_opt, size_t nb_sensor_opt)
+{
+    size_t i;
+
+    /* options */
+    printf(".Pp\nOPTIONS:\n.Bl -tag -width Ds\n");
+    for (i = 0; i < nb_opt; i++) {
+        dumpopt(&opts[i]);
+    }
+    printf(".El\n");
+
+    /* sensors */
+    printf(".Pp\nSENSORS:\n.Bl -tag -width Ds\n");
+    for (i++; i < nb_opt + nb_sensor_opt; i++) {
+        dumpopt(&opts[i]);
+    }
+    printf(".El\n");
+}
+
+void printopt(Optparse *opt)
+{
+    printf("-%c", opt->shortname);
+    printf("|--%s", opt->longname);
+    if (opt->usage_arg != NULL) {
+        printf(" %s", opt->usage_arg);
+    }
+    printf("\n\t%s\n", opt->usage_msg);
+}
+
+void usage(char **argv)
+{
+    printf("Usage : %s [OPTIONS] [SENSOR ...] [-e <cmd> ...]\n", argv[0]);
+
+    printf("\nOPTIONS:\n");
+    for (int i = 0; i < NB_OPT; i++) {
+        printopt(&opts[i]);
+    }
+
+    if (nb_defined_sensors == 0) {
+        // no captor to show
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\nSENSORS:\n");
+    for (int i = 0; i < NB_SENSOR_OPT; i++) {
+        printopt(&opts[NB_OPT + i]);
+    }
+
+    exit(EXIT_FAILURE);
+}
+
+void sighandler(int none)
+{
+    UNUSED(none);
+}
+
+void flush(int none)
+{
+    UNUSED(none);
+    exit(0);
+}
+
+FILE *output;
+void flushexit()
+{
+    if (output != NULL) {
+        fflush(output);
+        fclose(output);
+    }
+}
+
+unsigned int nb_sources = 0;
+void **states = NULL;
+getter_t *getter = NULL;
+cleaner_t *cleaner = NULL;
+
+unsigned int nb_sensors = 0;
+char **labels = NULL;
+uint64_t *values = NULL;
+
+void add_source(Sensor *cpt, char *arg)
+{
+    nb_sources++;
+    initializer_t init = cpt->init;
+    labeler_t labeler = cpt->label;
+    getter_t get = cpt->get;
+    cleaner_t clean = cpt->clean;
+
+    states = realloc(states, nb_sources * sizeof(void *));
+    int nb = init(arg, &states[nb_sources - 1]);
+
+    if (nb == 0) {
+        nb_sources--;
+        states = realloc(states, nb_sources * sizeof(void *));
+        return;
+    }
+
+    getter = realloc(getter, nb_sources * sizeof(void *));
+    getter[nb_sources - 1] = get;
+    cleaner = realloc(cleaner, nb_sources * sizeof(void *));
+    cleaner[nb_sources - 1] = clean;
+
+    labels = realloc(labels, (nb_sensors + nb) * sizeof(char *));
+    labeler(labels + nb_sensors, states[nb_sources - 1]);
+
+    values = realloc(values, (nb_sensors + nb) * sizeof(uint64_t));
+    nb_sensors += nb;
+}
+
+int main(int argc, char **argv)
+{
+    int total_time = 1;
+    int delta = 0;
+    int frequency = 1;
+    char **application = NULL;
+    int stat_mode = -1;
+
+    init_sensors(opts, sensors, NB_OPT + NB_SENSOR_OPT, NB_OPT, &nb_defined_sensors);
+
+    if (argc == 1) {
+        usage(argv);
+    }
+
+    if (argc == 2 && argv[1][0] == '-' && argv[1][1] == '1' && argv[1][2] == '\0') {
+        dumpopts(opts, NB_OPT, NB_SENSOR_OPT);
+        exit(EXIT_SUCCESS);
+    }
+
+    output = stdout;
+
+    atexit(flushexit);
+    signal(15, flush);
+
+    int opt;
+    struct optparse options;
+    options.permute = 0;
+
+    optparse_init(&options, argv);
+    while ((opt = optparse_long(&options, opts, NULL)) != -1 && application == NULL) {
+        switch (opt) {
+        case 'f':
+            frequency = atoi(options.optarg);
+            break;
+        case 't':
+            total_time = atoi(options.optarg);
+            delta = 1;
+            if (total_time == 0) {
+                total_time = 1;
+                delta = 0;
+            }
+            break;
+        case 's':
+            stat_mode = 0;
+            break;
+        case 'o':
+            if ((output = fopen(options.optarg, "wb")) == NULL) {
+                perror("fopen");
+                PANIC(1, "-o %s", options.optarg);
+            }
+            break;
+        case 'e':
+            application = options.argv;
+            signal(17, sighandler);
+            break;
+        default: {
+            int ismatch = 0;
+            int opt_idx = NB_OPT;
+            for (int i = 0; i < nb_defined_sensors && !ismatch; i++) {
+                for (int j = 0; j < sensors[i].nb_opt; j++) {
+                    if (opt == opts[opt_idx].shortname) {
+                        ismatch = 1;
+                        if (opts[opt_idx].fn != NULL) {
+                            (void) opts[opt_idx].fn(NULL, 0);
+                        } else {
+                            add_source(&sensors[i], options.optarg);
+                        }
+                        break;
+                    }
+                    opt_idx++;
+                }
+            }
+            if (!ismatch) {
+                fprintf(stderr, "%s: %s\n", argv[0], options.errmsg);
+                usage(argv);
+            }
+        }
+        }
+    }
+
+    setvbuf(output, NULL, _IONBF, BUFSIZ);
+    struct timespec ts;
+    struct timespec ts_ref;
+
+    fprintf(output, "#timestamp ");
+
+    for (unsigned int i = 0; i < nb_sensors; i++) {
+        fprintf(output, "%s ", labels[i]);
+    }
+
+    if (stat_mode == 0) {
+        fprintf(output, "overhead ");
+    }
+
+    fprintf(output, "\n");
+
+    unsigned long int stat_data = 0;
+
+    for (int temps = 0; temps < total_time * frequency; temps += delta) {
+        clock_gettime(CLOCK_MONOTONIC, &ts_ref);
+
+        // Get Data
+        unsigned int current = 0;
+
+        for (unsigned int i = 0; i < nb_sources; i++) {
+            current += getter[i](&values[current], states[i]);
+        }
+
+        if (application != NULL) {
+
+            if (fork() == 0) {
+                execvp(application[0], application);
+                exit(0);
+            }
+
+            pause();
+            clock_gettime(CLOCK_MONOTONIC, &ts);
+
+            if (ts.tv_nsec >= ts_ref.tv_nsec) {
+                fprintf(output, "%ld.%09ld ", (ts.tv_sec - ts_ref.tv_sec), ts.tv_nsec - ts_ref.tv_nsec);
+            } else {
+                fprintf(output, "%ld.%09ld ", (ts.tv_sec - ts_ref.tv_sec) - 1, 1000 * 1000 * 1000 + ts.tv_nsec - ts_ref.tv_nsec);
+            }
+        } else {
+#ifdef DEBUG
+            clock_gettime(CLOCK_MONOTONIC, &ts);
+            fprintf(stderr, "%ld\n", (ts.tv_nsec - ts_ref.tv_nsec) / 1000);
+            //Indiv: mean: 148 std: 31 % med: 141 std: 28 %
+            //Group: mean: 309 std: 41 % med: 297 std: 39 %
+#endif
+
+            if (stat_mode == 0) {
+                clock_gettime(CLOCK_MONOTONIC, &ts);
+
+                if (ts.tv_nsec >= ts_ref.tv_nsec) {
+                    stat_data = ts.tv_nsec - ts_ref.tv_nsec;
+                } else {
+                    stat_data = 1000 * 1000 * 1000 + ts.tv_nsec - ts_ref.tv_nsec;
+                }
+            }
+
+            // Treat Data
+            fprintf(output, "%ld.%09ld ", ts_ref.tv_sec, ts_ref.tv_nsec);
+        }
+
+        for (unsigned int i = 0; i < nb_sensors; i++) {
+            /* "PRIu64" is a format specifier to print uint64_t values */
+            fprintf(output, "%" PRIu64 " ", values[i]);
+        }
+
+        if (stat_mode == 0) {
+            fprintf(output, "%ld ", stat_data);
+        }
+
+        fprintf(output, "\n");
+
+        if (application != NULL) {
+            break;
+        }
+
+        clock_gettime(CLOCK_MONOTONIC, &ts);
+        usleep(1000 * 1000 / frequency - (ts.tv_nsec / 1000) % (1000 * 1000 / frequency));
+    }
+
+    for (unsigned int i = 0; i < nb_sources; i++) {
+        cleaner[i](states[i]);
+    }
+
+    if (nb_sources > 0) {
+        free(getter);
+        free(cleaner);
+        free(labels);
+        free(values);
+        free(states);
+    }
+}
+
diff --git a/src/network.c b/src/network.c
new file mode 100644
index 0000000000000000000000000000000000000000..37d237376be4c893e2c29de8d160b61ba3190628
--- /dev/null
+++ b/src/network.c
@@ -0,0 +1,146 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include "util.h"
+
+#define NB_SENSOR 4
+
+static char *route = "/proc/net/route";
+struct Network {
+    uint64_t values[NB_SENSOR];
+    uint64_t tmp_values[NB_SENSOR];
+    int sources[NB_SENSOR];
+};
+typedef struct Network Network;
+
+unsigned int _get_network(uint64_t *results, int *sources)
+{
+    if (sources == NULL) {
+        return 0;
+    }
+
+    char buffer[128];
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        if (pread(sources[i], buffer, 127, 0) < 0) {
+            perror("pread");
+            exit(1);
+        }
+
+        results[i] = strtoull(buffer, NULL, 10);
+    }
+
+    return NB_SENSOR;
+}
+
+
+
+unsigned int init_network(char *dev, void **ptr)
+{
+    if (dev == NULL) {
+        return 0;
+    }
+
+    if (strcmp(dev, "X") == 0) {
+        int fd = open(route, O_RDONLY);
+
+        if (fd < 0) {
+            fprintf(stderr, "%s ", route);
+            perror("open");
+            exit(1);
+        }
+
+        char buffer[1000];
+
+        if (read(fd, buffer, 999) < 0 ) {
+            perror("read");
+            close(fd);
+            exit(1);
+        }
+
+        char *start_of_dev = index(buffer, '\n') + 1;
+        char *end_of_dev = index(start_of_dev, '\t');
+        *end_of_dev = '\0';
+        dev = start_of_dev;
+        close(fd);
+    }
+
+    char *filenames[] = {"/sys/class/net/%s/statistics/rx_packets",
+                         "/sys/class/net/%s/statistics/rx_bytes",
+                         "/sys/class/net/%s/statistics/tx_packets",
+                         "/sys/class/net/%s/statistics/tx_bytes"
+                        };
+
+    Network *state = malloc(sizeof(Network));
+
+    char buffer2[256];
+    for (int i = 0; i < NB_SENSOR; i++) {
+        snprintf(buffer2, 256, filenames[i], dev);
+        state->sources[i] = open(buffer2, O_RDONLY);
+    }
+
+    *ptr = (void *) state;
+    _get_network(state->values, state->sources);
+
+    return NB_SENSOR;
+}
+
+unsigned int get_network(uint64_t *results, void *ptr)
+{
+    Network *state = (Network *) ptr;
+    _get_network(state->tmp_values, state->sources);
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        results[i] = modulo_substraction(state->tmp_values[i], state->values[i]);
+    }
+
+    memcpy(state->values, state->tmp_values, NB_SENSOR * sizeof(uint64_t));
+    return NB_SENSOR;
+}
+
+void clean_network(void *ptr)
+{
+    Network *state = (Network *) ptr;
+
+    if (state == NULL) {
+        return;
+    }
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        close(state->sources[i]);
+    }
+
+    free(state);
+}
+
+char *_labels_network[NB_SENSOR] = {"rxp", "rxb", "txp", "txb"};
+void label_network(char **labels, void *none)
+{
+    UNUSED(none);
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        labels[i] = _labels_network[i];
+    }
+}
diff --git a/network.h b/src/network.h
similarity index 56%
rename from network.h
rename to src/network.h
index 44759dcf883ac66c90aafdf6447d00d6b02e9a39..a158dbbce25333edf64c5f5dd0cde42856e08c70 100644
--- a/network.h
+++ b/src/network.h
@@ -1,5 +1,5 @@
 /*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
 
     This file is part of Mojitos.
 
@@ -18,8 +18,25 @@
 
  *******************************************************/
 
-unsigned int init_network(char*, void **);
-unsigned int get_network(uint64_t* results, void*);
+unsigned int init_network(char *, void **);
+unsigned int get_network(uint64_t *results, void *);
 void clean_network(void *);
-void label_network(char **labels, void*);
+void label_network(char **labels, void *);
 
+Sensor network = {
+    .init = init_network,
+    .get = get_network,
+    .clean = clean_network,
+    .label = label_network,
+    .nb_opt = 1,
+};
+
+Optparse network_opt[1] = {
+    {
+        .longname = "net-dev",
+        .shortname = 'd',
+        .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<net_dev>",
+        .usage_msg = "network monitoring (if network_device is X, tries to detect it automatically)",
+    },
+};
diff --git a/src/rapl.c b/src/rapl.c
new file mode 100644
index 0000000000000000000000000000000000000000..3d13367a47fb009b7f0a0f1567a9169daf9a08a4
--- /dev/null
+++ b/src/rapl.c
@@ -0,0 +1,194 @@
+/*******************************************************
+ Copyright (C) 2022-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
+#include "util.h"
+
+#define MAX_HEADER 128
+#define BUFFER_SIZE 1024
+
+char *get_rapl_string(const char *filename)
+{
+    int fd = open(filename, O_RDONLY);
+    if (fd == -1) {
+        return NULL;
+    }
+
+    char *result = malloc(MAX_HEADER);
+    int nb = read(fd, result, MAX_HEADER);
+    close(fd);
+    result[nb - 1] = 0;
+    return result;
+}
+
+void append(char *name, int i, size_t buffer_size)
+{
+    size_t name_len = strlen(name);
+    char *ptr = name + name_len;
+
+    size_t remaining_space = buffer_size - name_len;
+    snprintf(ptr, remaining_space, "%d", i);
+}
+
+
+struct IntelRapl {
+    unsigned int nb;
+    char **names;
+    int *fids;
+    uint64_t *values;
+    uint64_t *tmp_values;
+
+};
+typedef struct IntelRapl IntelRapl;
+
+
+void add_rapl_source(IntelRapl *rapl, char *name, char *energy_uj)
+{
+    rapl->nb += 1;
+    rapl->names = realloc(rapl->names, sizeof(char **)*rapl->nb);
+    rapl->fids = realloc(rapl->fids, sizeof(int *)*rapl->nb);
+
+    rapl->names[rapl->nb - 1] = malloc(strlen(name) + 1);
+    strcpy(rapl->names[rapl->nb - 1], name);
+    //printf("%s\n", energy_uj);
+
+    int fd = open(energy_uj, O_RDONLY);
+
+    if (fd < 0) {
+        fprintf(stderr, "%s ", energy_uj);
+        perror("open");
+        exit(1);
+    }
+
+    rapl->fids[rapl->nb - 1] = fd;
+}
+
+
+void _get_rapl(uint64_t *values, IntelRapl *rapl)
+{
+    static char buffer[512];
+
+    for (unsigned int i = 0; i < rapl->nb; i++) {
+
+        if (pread(rapl->fids[i], buffer, 100, 0) < 0) {
+            perror("pread");
+            exit(1);
+        }
+
+        values[i] = strtoull(buffer, NULL, 10);
+    }
+}
+
+
+unsigned int init_rapl(char *none, void **ptr)
+{
+    UNUSED(none);
+    IntelRapl *rapl = malloc(sizeof(IntelRapl));
+    rapl->nb = 0;
+    rapl->names = NULL;
+    rapl->fids = NULL;
+
+    char buffer[BUFFER_SIZE];
+    char *name_base = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:%d/%s";
+    char *name_sub = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:%d/intel-rapl:%d:%d/%s";
+
+    for (unsigned int i = 0;; i++) {
+        snprintf(buffer, BUFFER_SIZE, name_base, i, "name");
+        char *tmp = get_rapl_string(buffer);
+
+        if (tmp == NULL) {
+            break;
+        }
+
+        append(tmp, i, MAX_HEADER);
+        snprintf(buffer, BUFFER_SIZE, name_base, i, "energy_uj");
+        add_rapl_source(rapl, tmp, buffer);
+        free(tmp);
+
+        for (unsigned int j = 0;; j++) {
+            snprintf(buffer, BUFFER_SIZE, name_sub, i, i, j, "name");
+            char *tmp_sub = get_rapl_string(buffer);
+
+            if (tmp_sub == NULL) {
+                break;
+            }
+
+            append(tmp_sub, i, MAX_HEADER);
+            snprintf(buffer, BUFFER_SIZE, name_sub, i, i, j, "energy_uj");
+            add_rapl_source(rapl, tmp_sub, buffer);
+
+            free(tmp_sub);
+        }
+    }
+
+    rapl->values = calloc(sizeof(uint64_t), rapl->nb);
+    rapl->tmp_values = calloc(sizeof(uint64_t), rapl->nb);
+
+    _get_rapl(rapl->values, rapl);
+
+    *ptr = (void *)rapl;
+    return rapl->nb;
+}
+
+
+unsigned int get_rapl(uint64_t *results, void *ptr)
+{
+    IntelRapl *state = (IntelRapl *) ptr;
+    _get_rapl(state->tmp_values, state);
+
+    for (unsigned int i = 0; i < state->nb; i++) {
+        results[i] = modulo_substraction(state->tmp_values[i], state->values[i]);
+    }
+
+    memcpy(state->values, state->tmp_values, sizeof(uint64_t)*state->nb);
+    return state->nb;
+}
+
+void clean_rapl(void *ptr)
+{
+    IntelRapl *rapl = (IntelRapl *) ptr;
+
+    for (unsigned int i = 0; i < rapl->nb; i++) {
+        free(rapl->names[i]);
+        close(rapl->fids[i]);
+    }
+
+    free(rapl->names);
+    free(rapl->fids);
+    free(rapl->values);
+    free(rapl->tmp_values);
+    free(rapl);
+}
+
+
+void label_rapl(char **labels, void *ptr)
+{
+    IntelRapl *rapl = (IntelRapl *) ptr;
+
+    for (unsigned int i = 0; i < rapl->nb; i++) {
+        labels[i] = rapl->names[i];
+    }
+}
diff --git a/rapl.h b/src/rapl.h
similarity index 61%
rename from rapl.h
rename to src/rapl.h
index 9ec5f3afdc88d5f1bcdd4dcb5fdb4208261e2db0..7720f071a4be6b98dad5cb72d537bd69a430799d 100644
--- a/rapl.h
+++ b/src/rapl.h
@@ -1,5 +1,5 @@
 /*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
+ Copyright (C) 2022-2023 Georges Da Costa <georges.da-costa@irit.fr>
 
     This file is part of Mojitos.
 
@@ -18,8 +18,26 @@
 
  *******************************************************/
 
-unsigned int init_rapl(char*, void **);
-unsigned int get_rapl(uint64_t* results, void*);
+unsigned int init_rapl(char *, void **);
+unsigned int get_rapl(uint64_t *results, void *);
 void clean_rapl(void *);
-void label_rapl(char **labels, void*);
+void label_rapl(char **labels, void *);
+
+Sensor rapl = {
+    .init = init_rapl,
+    .get = get_rapl,
+    .clean = clean_rapl,
+    .label = label_rapl,
+    .nb_opt = 1,
+};
+
+Optparse rapl_opt[1] = {
+    {
+        .longname = "intel-rapl",
+        .shortname = 'r',
+        .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "INTEL RAPL",
+    },
+};
 
diff --git a/src/temperature.c b/src/temperature.c
new file mode 100644
index 0000000000000000000000000000000000000000..1dc1099d15e73128ec9d5124b521b958ae9efe46
--- /dev/null
+++ b/src/temperature.c
@@ -0,0 +1,177 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include "util.h"
+
+#define BUFFER_SIZE 512
+
+struct Temperature {
+    char **label_list;
+    int *fid_list;
+    int nb_elem;
+};
+typedef struct Temperature Temperature;
+
+int get_string(char *filename, char *buffer, int max_size)
+{
+    int fid = open(filename, O_RDONLY);
+
+    //printf("Tries to open : %s : %d\n", filename, fid);
+    if (fid == -1) {
+        return -1;
+    }
+
+    int nb = read(fid, buffer, max_size);
+    if (nb == -1) {
+        close(fid);
+        return -1;
+    }
+
+    buffer[nb] = 0;
+    close(fid);
+    return 0;
+}
+
+void add_to_list(char ***list_name, char *source, int nb_elem)
+{
+    //printf("Adds: %s\n", source);
+    *list_name = realloc(*list_name, (nb_elem + 1) * sizeof(char *));
+    (*list_name)[nb_elem] = malloc(strlen(source) + 1);
+    strcpy((*list_name)[nb_elem], source);
+
+}
+
+void add_temperature_sensor(int id_rep, Temperature *state)
+{
+    static int key = 0;
+    static char buffer_filename[BUFFER_SIZE];
+    static char buffer_label[BUFFER_SIZE];
+
+    int delta = snprintf(buffer_label, BUFFER_SIZE, "Temp_%d_", key);
+
+    for (int i = 1;; i++) {
+        snprintf(buffer_filename, BUFFER_SIZE, "/sys/class/hwmon/hwmon%d/temp%d_label", id_rep, i);
+
+        if (get_string(buffer_filename, buffer_label + delta, 100) == -1) {
+            break;
+        }
+
+        for (unsigned int pos = 0; pos < strlen(buffer_label); pos++) {
+            if (buffer_label[pos] == ' ') {
+                buffer_label[pos] = '_';
+            }
+
+            if (buffer_label[pos] == '\n') {
+                buffer_label[pos] = '\0';
+            }
+        }
+
+        add_to_list(&state->label_list, buffer_label, state->nb_elem);
+
+        snprintf(buffer_filename, BUFFER_SIZE, "/sys/class/hwmon/hwmon%d/temp%d_input", id_rep, i);
+        state->fid_list = realloc(state->fid_list, (state->nb_elem + 1) * sizeof(int));
+        int fd = open(buffer_filename, O_RDONLY);
+
+        if (fd < 0) {
+            fprintf(stderr, "%s ", buffer_filename);
+            perror("open");
+            exit(1);
+        }
+
+        state->fid_list[state->nb_elem] = fd;
+        state->nb_elem++;
+        // printf("%s : %s\n", buffer_label, buffer_filename);
+    }
+
+    key++;
+}
+
+unsigned int init_temperature(char *args, void **ptr)
+{
+    UNUSED(args);
+    Temperature *state = malloc(sizeof(Temperature));
+    state->nb_elem = 0;
+    state->label_list = NULL;
+    state->fid_list = NULL;
+
+    char base_name[] = "/sys/class/hwmon/hwmon%d/name";
+    static char name[BUFFER_SIZE];
+    static char buffer[BUFFER_SIZE];
+
+    int i = 0;
+    snprintf(name, BUFFER_SIZE, base_name, i);
+
+    while (get_string(name, buffer, 8) != -1) {
+        if (strcmp(buffer, "coretemp") == 0) {
+            add_temperature_sensor(i, state);
+        }
+
+        i++;
+        snprintf(name, BUFFER_SIZE, base_name, i);
+    }
+
+    *ptr = (void *) state;
+    return state->nb_elem;
+}
+
+unsigned int get_temperature(uint64_t *results, void *ptr)
+{
+    Temperature *state = (Temperature *)ptr;
+    static char buffer[BUFFER_SIZE];
+
+    for (int i = 0; i < state->nb_elem; i++) {
+        if (pread(state->fid_list[i], buffer, 100, 0) < 0) {
+            perror("pread");
+            exit(1);
+        }
+        results[i] = strtoull(buffer, NULL, 10);
+    }
+
+    return state->nb_elem;
+}
+
+void clean_temperature(void *ptr)
+{
+    Temperature *state = (Temperature *)ptr;
+
+    for (int i = 0; i < state->nb_elem; i++) {
+        free(state->label_list[i]);
+        close(state->fid_list[i]);
+    }
+
+    free(state->label_list);
+    free(state->fid_list);
+    free(state);
+}
+
+void label_temperature(char **labels, void *ptr)
+{
+    Temperature *state = (Temperature *)ptr;
+
+    for (int i = 0; i < state->nb_elem; i++) {
+        labels[i] = state->label_list[i];
+    }
+}
diff --git a/temperature.h b/src/temperature.h
similarity index 58%
rename from temperature.h
rename to src/temperature.h
index c6c34df6c612e709764e2b7d9dfd37ec234ccbf5..e70554d17ab7878b788bbee3afa96da72620cfe2 100644
--- a/temperature.h
+++ b/src/temperature.h
@@ -1,5 +1,5 @@
 /*******************************************************
- Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr>
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
 
     This file is part of Mojitos.
 
@@ -18,7 +18,25 @@
 
  *******************************************************/
 
-unsigned int init_temperature(char*, void **);
-unsigned int get_temperature(uint64_t* results, void*);
+unsigned int init_temperature(char *, void **);
+unsigned int get_temperature(uint64_t *results, void *);
 void clean_temperature(void *);
-void label_temperature(char **labels, void*);
+void label_temperature(char **labels, void *);
+
+Sensor temperature = {
+    .init = init_temperature,
+    .get = get_temperature,
+    .clean = clean_temperature,
+    .label = label_temperature,
+    .nb_opt = 1,
+};
+
+Optparse temperature_opt[1] = {
+    {
+        .longname = "cpu-temp",
+        .shortname = 'c',
+        .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "processor temperature"
+    },
+};
diff --git a/frapl.h b/src/util.c
similarity index 77%
rename from frapl.h
rename to src/util.c
index 6b2d15424723ba2f670718525e294aa75eaba90e..5e835a7f21583cdca51707b97ecf122aa3e4910f 100644
--- a/frapl.h
+++ b/src/util.c
@@ -16,10 +16,13 @@
     You should have received a copy of the GNU General Public License
     along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
 
- *******************************************************/
+*******************************************************/
 
-unsigned int init_frapl(char*, void **);
-unsigned int get_frapl(uint64_t* results, void*);
-void clean_frapl(void *);
-void label_frapl(char **labels, void*);
+#include "util.h"
 
+
+uint64_t modulo_substraction(const uint64_t lhs, const uint64_t rhs)
+{
+    return lhs >= rhs ? (lhs - rhs)
+           : (UINT64_MAX - rhs + 1) + lhs;
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000000000000000000000000000000000000..e422d070693cd28af9613e77a5efdb917ce6b2f6
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,51 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+*******************************************************/
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <stdint.h>
+
+#define CASSERT(predicate, file) _impl_CASSERT_LINE(predicate,__LINE__,file)
+
+#define _impl_PASTE(a,b) a##b
+#define _impl_CASSERT_LINE(predicate, line, file) \
+    typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];
+
+#define UNUSED(expr) do { (void)(expr); } while (0)
+#define PANIC(code, fmt, ...)                \
+    do {                                     \
+        fprintf(stderr, "Exit on error: ");  \
+        fprintf(stderr, fmt, ##__VA_ARGS__); \
+        fprintf(stderr, "\n");               \
+        exit(code);                          \
+    } while (0)
+
+
+/**
+ * @brief Substracts lhs by rhs, assuming that lhs is a cyclic increment from rhs,
+ * meaning that if rhs is greater, lhs's value overflowed.
+ * @param lhs
+ * @param rhs
+ * @return uint64_t
+ */
+uint64_t modulo_substraction(const uint64_t lhs, const uint64_t rhs);
+
+#endif
diff --git a/temperature.c b/temperature.c
deleted file mode 100644
index 142e86ae61604d920c92563196cffef9ad043386..0000000000000000000000000000000000000000
--- a/temperature.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdint.h>
-
-struct temperature_t {
-  char **label_list;
-  int *fid_list;
-  int nb_elem;
-};
-  
-int get_string(char *filename, char *buffer, int max_size) {
-  int fid = open(filename, O_RDONLY);
-  //printf("Tries to open : %s : %d\n", filename, fid);
-  if(fid == -1)
-    return -1;
-      
-  int nb = read(fid, buffer, max_size);
-  if(nb == -1) {
-    close(fid);
-    return -1;
-  }
-
-  buffer[nb]=0;
-  close(fid);
-  return 0;
-}
-
-void add_to_list(char ***list_name, char *source, int nb_elem) {
-  //printf("Adds: %s\n", source);
-  *list_name = realloc(*list_name, (nb_elem+1)*sizeof(char*));
-  (*list_name)[nb_elem] = malloc(strlen(source)+1);
-  strcpy((*list_name)[nb_elem], source);
-
-}
-
-void add_temperature_sensor(int id_rep, struct temperature_t *state) {
-  static int key=0;
-  static char buffer_filename[512];
-  static char buffer_label[512];
-  
-  int delta = sprintf(buffer_label, "Temp_%d_", key);
-  
-  for(int i=1;;i++) {
-    sprintf(buffer_filename, "/sys/class/hwmon/hwmon%d/temp%d_label", id_rep, i);
-    if(get_string(buffer_filename, buffer_label+delta, 100) == -1)
-      break;
-
-    for(int pos = 0; pos < strlen(buffer_label); pos++) {
-      if (buffer_label[pos] == ' ')  buffer_label[pos] = '_';
-      if (buffer_label[pos] == '\n') buffer_label[pos] = '\0';
-    }
-    add_to_list(&state->label_list, buffer_label, state->nb_elem);
-
-    sprintf(buffer_filename, "/sys/class/hwmon/hwmon%d/temp%d_input", id_rep, i);
-    state->fid_list = realloc(state->fid_list, (state->nb_elem+1)*sizeof(int));
-    state->fid_list[state->nb_elem] = open(buffer_filename, O_RDONLY);
-
-    state->nb_elem++;
-    // printf("%s : %s\n", buffer_label, buffer_filename);
-  }
-  
-  key++;
-}
-
-unsigned int init_temperature(char *args, void** ptr) {
-  struct temperature_t *state = malloc(sizeof(struct temperature_t));
-  state->nb_elem = 0;
-  state->label_list = NULL;
-  state->fid_list = NULL;
-  
-  char base_name[] = "/sys/class/hwmon/hwmon%d/name";
-  static char name[512];
-  static char buffer[512];
-
-  int i = 0;
-  sprintf(name, base_name, i);
-  while(get_string(name, buffer, 8) != -1) {
-    if (strcmp(buffer, "coretemp")==0)
-      add_temperature_sensor(i, state);
-
-    i++;
-    sprintf(name, base_name, i);
-  }
-  *ptr = (void*) state;
-  return state->nb_elem;
-}
-
-unsigned int get_temperature(uint64_t* results, void* ptr) {
-  struct temperature_t *state = (struct temperature_t*)ptr;
-  static char buffer[512];
-  for(int i=0; i<state->nb_elem; i++) {
-    pread(state->fid_list[i], buffer, 100, 0);
-    results[i] = strtoull(buffer, NULL, 10);
-  }
-  return state->nb_elem;
-}
-
-void clean_temperature(void* ptr) {
-  struct temperature_t *state = (struct temperature_t*)ptr;
-  
-  for(int i=0; i<state->nb_elem; i++) {
-    free(state->label_list[i]);
-    close(state->fid_list[i]);
-  }
-  free(state->label_list);
-  free(state->fid_list);
-  free(state);
-}
-
-void label_temperature(char**labels, void* ptr) {
-  struct temperature_t *state = (struct temperature_t*)ptr;
-  for(int i=0; i<state->nb_elem; i++)
-    labels[i] = state->label_list[i];
-}