diff --git a/README.md b/README.md
index 311ef4255a821b8e8174d5e781c58429e2a16b82..4ecd8dc0c8884fce56c825e883925eefae1ce57b 100644
--- a/README.md
+++ b/README.md
@@ -59,11 +59,15 @@ make
 ```
 You may want to run `./configure.sh --help` to see configuration options.
 
+To use `amd_rapl` you have to load the module `msr`
+```bash
+sudo modprobe msr
+```
 To execute mojitos without being root to monitor performance counters
 ```bash
 sudo sh -c 'echo 0 >/proc/sys/kernel/perf_event_paranoid'
 ```
-To execute mohitos without being root for accessing RAPL
+To execute mojitos without being root for accessing RAPL
 ```bash
 sudo chmod a+w /sys/class/powercap/intel-rapl/*/*
 sudo chmod a+w /sys/class/powercap/intel-rapl/*/*/*
diff --git a/configure.sh b/configure.sh
index 21c94eff4ef28cab03761595e6473b29c80e8b2b..203fd04a0e6663c096db02d5001d8192bf47f564 100755
--- a/configure.sh
+++ b/configure.sh
@@ -43,16 +43,16 @@ usage() {
 }
 
 ls_sensors() {
-	try cd src
+	[ -d src ] || die 'fatal: the "src" directory does not exit.'
 
 	[ -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$//'
+	try find src -type f -name '*.h' |
+		sed 's,src/\(.*\)\.h,\1,' |
+		grep -xEv "($hdr_blacklist)" |
+		grep -xE  "($hdr_whitelist)"
 }
 
 # gen_sensors_h(sensor, nb_sensors)
@@ -103,6 +103,11 @@ gen_sensors_mk() {
 		printf '$(OBJ_DIR)/%s.o ' "$sensor"
 	done
 	printf '\n'
+	for sensor in $sensors; do
+		printf '$(OBJ_DIR)/%s.o: $(SRC_DIR)/%s.c $(SRC_DIR)/%s.h $(SRC_DIR)/util.h\n' \
+			"$sensor" "$sensor" "$sensor"
+		printf '\t$(CC) $(CFLAGS) -c $< -o $@\n'
+	done
 }
 
 detect_caps() {
diff --git a/doc/counter_ex.h b/doc/counter_ex.h
new file mode 100644
index 0000000000000000000000000000000000000000..65fabf3756dddc1b41b3bb16d143b3158854c20f
--- /dev/null
+++ b/doc/counter_ex.h
@@ -0,0 +1,26 @@
+/*
+ * Example of a basic counter: 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 rapl = {
+    .init = init_acc,
+    .get = get_acc,
+    .clean = clean_acc,
+    .label = label_acc,
+    .nb_opt = 1,
+};
+
+Optparse rapl_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/doc/network.md b/doc/network.md
new file mode 100644
index 0000000000000000000000000000000000000000..b5d1e68304a72a35da213a74dfab918321663247
--- /dev/null
+++ b/doc/network.md
@@ -0,0 +1,8 @@
+This sensor can autodetect interfaces in use by giving the special
+interface name "X".  But the total number of interfaces it can autodetect
+is currently under a hard-limit.  This hard-limit can be changed by
+modifying this line in `src/network.c`:
+
+```c
+#define NB_MAX_DEV 8
+```
diff --git a/makefile b/makefile
index 66bcc991a83a7acdf75ff5258084f6b708b48d5f..71e22aae873ab7d98f98476ad667a382a770d288 100644
--- a/makefile
+++ b/makefile
@@ -7,6 +7,14 @@ BIN_DIR = bin
 TESTS_DIR = tests
 
 BIN = mojitos
+PREFIX = /usr/local
+
+CC = gcc
+CPPFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -Wno-unused-function -I./lib
+CFLAGS = $(CPPFLAGS) -O3 -Werror
+LDFLAGS =
+
+ASTYLE = astyle --style=kr -xf -s4 -k3 -n -Z -Q
 
 CAPTOR_OBJ =
 
@@ -29,7 +37,7 @@ $(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
+$(OBJ_DIR)/util.o: $(SRC_DIR)/util.c $(SRC_DIR)/util.h
 	$(CC) $(CFLAGS) -c $< -o $@
 
 $(SRC_DIR)/counters_option.h: $(SRC_DIR)/counters_option.sh
@@ -68,4 +76,16 @@ man: $(BIN)
 		'/^USAGE/ { $$0=usage } 1' \
 		doc/$(BIN).pre.1 > doc/$(BIN).1 2>/dev/null
 
-.PHONY: all clean mojitos debug format tests readme man
+install: $(BIN) man
+	mkdir -p $(PREFIX)/bin
+	cp $(BIN_DIR)/$(BIN) $(PREFIX)/bin/.
+	chmod 755 $(PREFIX)/bin/$(BIN)
+	mkdir -p $(PREFIX)/share/man/man1
+	cp $(DOC_DIR)/$(BIN).1 $(PREFIX)/share/man/man1/.
+	chmod 644 $(PREFIX)/share/man/man1/$(BIN).1
+
+uninstall:
+	rm -f $(PREFIX)/bin/$(BIN)
+	rm -f $(PREFIX)/share/man/man1/$(BIN).1
+
+.PHONY: all clean mojitos debug format tests readme man install uninstall
diff --git a/src/amd_rapl.c b/src/amd_rapl.c
index 1e08517d7d2f66c5b5404e973f13aed5f366d390..f08c5a73e80093b2d06fd13739e82796272b2cee 100644
--- a/src/amd_rapl.c
+++ b/src/amd_rapl.c
@@ -25,6 +25,7 @@
 #include <string.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <inttypes.h>
 
 #include "info_reader.h"
 #include "util.h"
@@ -120,7 +121,7 @@ uint64_t read_msr(int fd, uint64_t msr)
 {
     uint64_t data;
     if (pread(fd, &data, sizeof data, msr) != sizeof data) {
-        fprintf(stderr, "read_msr(%ld):", msr);
+        fprintf(stderr, "read_msr(%"PRIu64"):", msr);
         perror("pread");
         exit(127);
     }
@@ -175,7 +176,7 @@ uint64_t raw_to_joule(uint64_t raw, uint64_t unit)
 void debug_print_sensor(CpuSensor *sensor)
 {
     //CASSERT(sizeof(CpuSensor) == 56, amd_rapl_c);
-    printf("cpu_id : %d, package_id : %d, core_id : %d, name : %s, fd: %d,  energy_units : %d, core_energy: %ld\n",
+    printf("cpu_id : %d, package_id : %d, core_id : %d, name : %s, fd: %d,  energy_units : %d, core_energy: %"PRIu64"\n",
            sensor->cpu_id,
            sensor->package_id,
            sensor->core_id,
diff --git a/src/network.c b/src/network.c
index 37d237376be4c893e2c29de8d160b61ba3190628..a668725dcd51825c2245c34b7ae387cd7125b294 100644
--- a/src/network.c
+++ b/src/network.c
@@ -17,28 +17,40 @@
     along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
 
  *******************************************************/
-#include <unistd.h>
+#include <errno.h>
 #include <fcntl.h>
-#include <stdlib.h>
+#include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
-#include <stdint.h>
+#include <unistd.h>
+
 #include "util.h"
 
+#define NB_MAX_DEV 8
 #define NB_SENSOR 4
 
 static char *route = "/proc/net/route";
+char *_labels_network[NB_SENSOR] = {
+    "%s:rxp",
+    "%s:rxb",
+    "%s:txp",
+    "%s:txb",
+};
 struct Network {
-    uint64_t values[NB_SENSOR];
-    uint64_t tmp_values[NB_SENSOR];
-    int sources[NB_SENSOR];
+    uint64_t values[NB_MAX_DEV][NB_SENSOR];
+    uint64_t tmp_values[NB_MAX_DEV][NB_SENSOR];
+    int sources[NB_MAX_DEV][NB_SENSOR];
+    char labels[NB_MAX_DEV][NB_SENSOR][128];
+    char devs[NB_MAX_DEV][128];
+    int ndev;
 };
 typedef struct Network Network;
 
-unsigned int _get_network(uint64_t *results, int *sources)
+static void _get_network(uint64_t *results, int *sources)
 {
     if (sources == NULL) {
-        return 0;
+        return;
     }
 
     char buffer[128];
@@ -51,18 +63,51 @@ unsigned int _get_network(uint64_t *results, int *sources)
 
         results[i] = strtoull(buffer, NULL, 10);
     }
-
-    return NB_SENSOR;
 }
 
+/*
+ * read from fd len chars and store them into buf
+ * make *s points to the first occurence of c into buf
+*/
+static int strchr_refill(int fd, char *buf, int len, char **s, char c)
+{
+    *s = strchr(*s, c);
+
+    if (*s == NULL) {
+        int nbytes = read(fd, buf, len - 1);
+        if (nbytes < 0) {
+            perror("read");
+            return -1;
+        }
+        buf[len - 1] = '\0';
+
+        /* whole file read */
+        if (nbytes == 0) {
+            return 0;
+        }
 
+        *s = strchr(buf, c);
+    }
+
+    return 1;
+}
 
 unsigned int init_network(char *dev, void **ptr)
 {
     if (dev == NULL) {
-        return 0;
+        exit(1);
     }
 
+    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 *state = malloc(sizeof(struct Network));
+    memset(state, '\0', sizeof(*state));
+
     if (strcmp(dev, "X") == 0) {
         int fd = open(route, O_RDONLY);
 
@@ -74,50 +119,110 @@ unsigned int init_network(char *dev, void **ptr)
 
         char buffer[1000];
 
-        if (read(fd, buffer, 999) < 0 ) {
-            perror("read");
+        /* skip first line */
+        char *s = buffer;
+        int ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\n');
+        if (ret != 1) {
             close(fd);
+            free(state);
             exit(1);
         }
+        s++;
 
-        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 *start_of_dev = s;
+        /* jump to the end of the device name */
+        ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\t');
+        if (ret != 1) {
+            close(fd);
+            free(state);
+            exit(1);
+        }
 
-    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"
-                        };
+        state->ndev++;	// ndev should be equal to 1 at this point
+        memcpy(&(state->devs[state->ndev - 1]), start_of_dev,
+               MIN((size_t)(sizeof(state->devs[0]) - 1), (size_t)(s - start_of_dev)));
+
+        for (;;) {
+            /* jump to the next line */
+            ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\n');
+            if (ret != 1) {
+                break;
+            }
+            s++;
+
+            start_of_dev = s;
+            ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\t');
+            if (ret != 1) {
+                break;
+            }
+
+            /* compare dev name to the previously saved one */
+            int newdev = 1;
+            for (int i = 0; i < state->ndev && newdev; i++) {
+	            if (strncmp(start_of_dev, state->devs[i], s - start_of_dev) == 0) {
+	            	newdev = 0;
+	            }
+            }
+            if (newdev) {
+                if (state->ndev >= NB_MAX_DEV) {
+                    fprintf(stderr, "Maximum amount of network devices exceeded (%d).\n", NB_MAX_DEV);
+                    break;
+                }
+                state->ndev++;
+                memcpy(&(state->devs[state->ndev - 1]), start_of_dev,
+                       MIN((size_t)(sizeof(state->devs[0]) - 1), (size_t)(s - start_of_dev)));
+            }
+        }
 
-    Network *state = malloc(sizeof(Network));
+        close(fd);
+    } else {
+        state->ndev = 1;
+        memcpy(&(state->devs[0]), dev, strlen(dev) + 1);
+    }
 
     char buffer2[256];
-    for (int i = 0; i < NB_SENSOR; i++) {
-        snprintf(buffer2, 256, filenames[i], dev);
-        state->sources[i] = open(buffer2, O_RDONLY);
+    for (int i = 0; i < state->ndev; i++) {
+        for (int j = 0; j < NB_SENSOR; j++) {
+            snprintf(buffer2, sizeof(buffer2), filenames[j], state->devs[i]);
+            errno = 0;
+            int fd = open(buffer2, O_RDONLY);
+            if (fd < 0) {
+                fprintf(stderr, "init_network: open: %s: %.*s\n", strerror(errno),
+                        (int)sizeof(buffer2), buffer2);
+                free(state);
+                exit(1);
+            }
+            state->sources[i][j] = fd;
+            snprintf(state->labels[i][j], sizeof(state->labels[i][j]), _labels_network[j],
+                     state->devs[i]);
+        }
     }
 
     *ptr = (void *) state;
-    _get_network(state->values, state->sources);
 
-    return NB_SENSOR;
+    for (int i = 0; i < state->ndev; i++) {
+        _get_network(state->values[i], state->sources[i]);
+    }
+
+    return state->ndev * NB_SENSOR;
 }
 
 unsigned int get_network(uint64_t *results, void *ptr)
 {
-    Network *state = (Network *) ptr;
-    _get_network(state->tmp_values, state->sources);
+    struct Network *state = (struct Network *) ptr;
 
-    for (int i = 0; i < NB_SENSOR; i++) {
-        results[i] = modulo_substraction(state->tmp_values[i], state->values[i]);
+    for (int i = 0; i < state->ndev; i++) {
+        _get_network(state->tmp_values[i], state->sources[i]);
+
+        for (int j = 0; j < NB_SENSOR; j++) {
+            results[i*NB_SENSOR + j] = modulo_substraction(state->tmp_values[i][j], state->values[i][j]);
+        }
+
+        memcpy(&(state->values[i]), &(state->tmp_values[i]),
+               NB_SENSOR * sizeof(state->values[i][0]));
     }
 
-    memcpy(state->values, state->tmp_values, NB_SENSOR * sizeof(uint64_t));
-    return NB_SENSOR;
+    return state->ndev * NB_SENSOR;
 }
 
 void clean_network(void *ptr)
@@ -128,19 +233,22 @@ void clean_network(void *ptr)
         return;
     }
 
-    for (int i = 0; i < NB_SENSOR; i++) {
-        close(state->sources[i]);
+    for (int i = 0; i < state->ndev; i++) {
+        for (int j = 0; j < NB_SENSOR; j++) {
+            close(state->sources[i][j]);
+        }
     }
 
     free(state);
 }
 
-char *_labels_network[NB_SENSOR] = {"rxp", "rxb", "txp", "txb"};
-void label_network(char **labels, void *none)
+void label_network(char **labels, void *ptr)
 {
-    UNUSED(none);
+    struct Network *state = (struct Network *) ptr;
 
-    for (int i = 0; i < NB_SENSOR; i++) {
-        labels[i] = _labels_network[i];
+    for (int i = 0; i < state->ndev; i++) {
+        for (int j = 0; j < NB_SENSOR; j++) {
+            labels[i*NB_SENSOR + j] = state->labels[i][j];
+        }
     }
 }
diff --git a/src/util.h b/src/util.h
index e422d070693cd28af9613e77a5efdb917ce6b2f6..0015a9867452800633582cdbd9736ab5c11c145e 100644
--- a/src/util.h
+++ b/src/util.h
@@ -38,6 +38,8 @@
         exit(code);                          \
     } while (0)
 
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
 
 /**
  * @brief Substracts lhs by rhs, assuming that lhs is a cyclic increment from rhs,
diff --git a/tests/g5k/README b/tests/g5k/README
new file mode 100644
index 0000000000000000000000000000000000000000..01b0d765120a15974c62892d542b4c243913efe3
--- /dev/null
+++ b/tests/g5k/README
@@ -0,0 +1,96 @@
+This directory is for tests against the g5k (grid5000) infrastructure.
+It should contain the "run" script, "ssh.pre.config", test executables,
+and a configuration file named "config".
+
+
+CONFIGURATION
+--------------
+
+The configuration file contains records (lines) of colon-separated
+fields. These fields are, in order: a g5k site, a g5k cluster (or machine
+name) and a test executable filename. All blank lines, or lines beginning
+with a '#' shall be ignored.
+
+For instance, the following record specifies that the executable
+"test-load.sh" should be run on the "lille" g5k site and on the "chifflet"
+g5k cluster:
+
+	lille:chifflet:test-load.sh
+
+As another example, the following example specifies that the executable
+"test-infiniband.sh" should be run on the "nancy" site and on the
+"grisou-49" machine (from the "grisou" cluster):
+
+	nancy:grisou-49:test-infiniband.sh
+
+
+RUN
+---
+
+"run" is an helper script to run specified tests against the g5k infrastructure.
+
+Usage:
+
+	run -u <user> -i <identity_file> [<test_name_re> ...]
+
+where "user" is your g5k's login name, and "identity_file" would usually
+be something like "~/.ssh/grid5000.id_rsa" (in other words, this your
+g5k's private key).
+
+By default, without any other arguments, it will run all tests defined in
+the config file. If you specify any further arguments, it will interpret
+them as regular expressions and look for matches through the config file.
+
+Test results are sent back as their standard output and standard error
+output combined, each line prefixed with: "<test_filename>:". Therefore,
+one can have the output of, say, "test-load.sh", by piping `run` into
+`grep '^test-load.sh' | cut -d ':' -f 2-`.
+
+
+EXAMPLES
+--------
+
+In the following examples, the given example config is supposed:
+
+```
+# site:cluster:test-filename
+
+lille:chifflet:test-nvidia.sh
+lille:chifflet:test-rapl.sh
+lille:chifflet:test-counters.sh
+lille:chifflet:test-temp.sh
+lille:chifflet:test-load.sh
+lille:chifflet:test-network.sh
+
+lille:chiclet:test-amd-rapl.sh
+lille:chiclet:test-infiniband.sh
+
+nancy:grisou-49:test-infiniband.sh
+```
+
+Run "test-load" test:
+
+	./run -u joe -i idfile test-load
+
+Run "test-counters" and "test-rapl" tests (notice that we write
+"test-rapl" in the command and not simply "rapl", because if we don't,
+both "test-rapl" and "test-amd-rapl" would be executed):
+
+	./run -u joe -i idfile counters test-rapl
+
+Run all lille tests:
+
+	./run -u joe -i idfile lille
+
+Run infiniband test on nancy site:
+
+	./run -u joe -i idfile 'nancy.*infiniband'
+
+Run infiniband test on all specified sites:
+
+	./run -u joe -i idfile infiniband
+
+Run all tests which are defined to use the chiclet cluster:
+
+	./run -u joe -i idfile chiclet
+
diff --git a/tests/g5k/config b/tests/g5k/config
new file mode 100644
index 0000000000000000000000000000000000000000..feb1c26d78c12a347354e1e562a46351b40ac20f
--- /dev/null
+++ b/tests/g5k/config
@@ -0,0 +1,13 @@
+# site:cluster:test-filename
+
+lille:chifflet:test-nvidia.sh
+lille:chifflet:test-rapl.sh
+lille:chifflet:test-counters.sh
+lille:chifflet:test-temp.sh
+lille:chifflet:test-load.sh
+lille:chifflet:test-network.sh
+
+lille:chiclet:test-amd-rapl.sh
+lille:chiclet:test-infiniband.sh
+
+nancy:grisou-49:test-infiniband.sh
diff --git a/tests/g5k/run b/tests/g5k/run
new file mode 100755
index 0000000000000000000000000000000000000000..1cc7af7204e296a29dcab873d451bb668d1af1f6
--- /dev/null
+++ b/tests/g5k/run
@@ -0,0 +1,132 @@
+#!/bin/sh
+
+try() { "$@" || die "cannot $*"; }
+die() { yell "$*"; exit 111; }
+yell() { echo "$0: $*" >&2; }
+echo() { printf '%s\n' "$*"; }
+
+base=$(dirname "$0")
+configf=${base}/config
+ssh_conf_pre=${base}/ssh.pre.config
+ssh_conf=${base}/ssh.config
+
+usage() {
+	printf 'usage: %s -u <user> -i <identity_file> [<test_name_re> ...]\n' "$(basename "$0")" >&2
+	exit 1
+}
+
+sanitize_config() {
+	sed '/^#/d; /^$/d'
+}
+
+while [ "$1" ]; do
+	case $1 in
+	-u)
+		shift
+		[ "$1" ] || usage
+		user=$1
+		;;
+	-i)
+		shift
+		[ "$1" ] || usage
+		[ -r "$1" ] || { yell "file not readable: $1"; usage; }
+		idfile=$1
+		;;
+	*)
+		test_name_re=$1
+		break;
+		;;
+	esac
+	shift
+done
+
+[ -z "$user" ] && { yell 'missing user parameter'; usage; }
+[ -z "$idfile" ] && { yell 'missing identity_file parameter'; usage; }
+
+if [ $# -eq 0 ]; then
+	# grab all available tests by default
+	config=$(sanitize_config < "$configf")
+else
+	re='('
+	for test_name_re; do
+		re="${re}${test_name_re}|"
+	done
+	re="${re%|})"
+	config=$(grep -E "$re" "$configf" | sanitize_config)
+fi
+
+[ -n "$config" ] || die 'no valid sensors specified. exiting..'
+config=$(echo "$config" | sort -u)
+
+sed "
+	s,USER,$user,
+	s,IDFILE,$idfile,
+" "$ssh_conf_pre" > "$ssh_conf"
+
+sites=$(echo "$config" | cut -d ':' -f 1 | sort -u)
+for site in $sites; do
+	site_runner=${base}/${site}-run.sh
+	clusters=$(echo "$config" | grep "^${site}:" | cut -d ':' -f 2 | sort -u)
+
+	try cp -f "${base}/site-runner-common.sh" "$site_runner"
+
+	for cluster in $clusters; do
+		files=$(echo "$config" | grep "^${site}:${cluster}:" | cut -d ':' -f 3 | sort -u)
+		cluster_runner=${base}/${site}-${cluster}-run.sh
+
+		echo '#!/bin/sh' > "$cluster_runner"
+		echo 'exec 2>&1' >> "$cluster_runner"
+
+		for f in $files; do
+			[ -r "$f" ] || {
+				yell "warning: file not readable: $f"
+				continue
+			}
+			destf=${cluster}-${f}
+
+			scp -F "$ssh_conf" "$f" "${site}.g5k:${destf}" || continue
+
+			{
+				printf '{\n'
+				printf '\tchmod +x "$HOME/%s"\n' "$destf"
+				printf '\t$HOME/%s\n' "$destf"
+				printf "} | awk -v 'prefix=%s' '%s'\n" \
+					"$destf" '{printf("%s:%s\n", prefix, $0)}'
+			} >> "$cluster_runner"
+		done
+
+		scp -F "$ssh_conf" "$cluster_runner" "${site}.g5k:${cluster}-run.sh" ||
+			continue
+
+		{
+			printf 'chmod +x "$HOME/%s-run.sh"\n' "$cluster"
+			printf 'eval "$(oarsub -p "%s" "~/%s-run.sh")"\n' "$cluster" "$cluster"
+			printf 'echo $OAR_JOB_ID\n'
+		} >> "$site_runner"
+	done
+
+	scp -F "$ssh_conf" "$site_runner" "${site}.g5k:${site}-run.sh" ||
+		continue
+
+	oar_job_ids=$(ssh -F "$ssh_conf" "${site}.g5k" "./${site}-run.sh")
+
+	sleep 3
+	while {
+		for id in $oar_job_ids; do
+			# job is no longer running
+			if ssh -F "$ssh_conf" "${site}.g5k" \
+				"! { oarstat -u \"$user\" | grep -q \"$id\"; }"
+			then
+				scp -F "$ssh_conf" "${site}.g5k:OAR.${id}.stdout" "OAR.${id}.stdout" &&
+					cat "OAR.${id}.stdout" &&
+					rm -f "OAR.${id}.stdout"
+				oar_job_ids=$(echo "$oar_job_ids" | grep -vF "$id")
+			fi
+		done
+		[ -n "$oar_job_ids" ]
+	}; do
+		sleep 3
+	done
+done
+
+exit
diff --git a/tests/g5k/site-runner-common.sh b/tests/g5k/site-runner-common.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f8d75fa78af2fc6e6a5ff1362233bbba005688da
--- /dev/null
+++ b/tests/g5k/site-runner-common.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+echo() { printf '%s\n' "$*"; }
+try() { "$@" || die "cannot $*"; }
+die() { yell "$*"; exit 111; }
+yell() { echo "$0: $*" >&2; }
+
+repo_dir=mojitos
+repo_url=https://gitlab.irit.fr/sepia-pub/mojitos.git
+branch=test
+
+if [ -d "$repo_dir" ]; then
+	fetch_url=$(git -C "$repo_dir" remote -v | awk 'NR == 1 {print $2}')
+
+	if [ "$fetch_url" != "$repo_url" ]; then
+		echo 'bad fetch url. recloning..' >&2
+		rm -rf "$repo_dir"
+		try git clone -b "$branch" "$repo_url" "$repo_dir"
+	fi
+else
+	try git clone -b "$branch" "$repo_url" "$repo_dir"
+fi
+
diff --git a/tests/g5k/ssh.pre.config b/tests/g5k/ssh.pre.config
new file mode 100644
index 0000000000000000000000000000000000000000..1f466f53c4d09a8335490224151f0b94afb597ed
--- /dev/null
+++ b/tests/g5k/ssh.pre.config
@@ -0,0 +1,11 @@
+Host g5k
+  User USER
+  Hostname access.grid5000.fr
+  IdentityFile IDFILE
+  ForwardAgent no
+
+Host *.g5k
+  User USER
+  ProxyCommand ssh g5k -W "$(basename %h .g5k):%p"
+  IdentityFile IDFILE
+  ForwardAgent no
diff --git a/tests/g5k/test-amd-rapl.sh b/tests/g5k/test-amd-rapl.sh
new file mode 100755
index 0000000000000000000000000000000000000000..aaa9a74e7ad5c32c07a61498e93e8fc62f15d62b
--- /dev/null
+++ b/tests/g5k/test-amd-rapl.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo testing amd rapl sensor
+hostname
diff --git a/tests/g5k/test-counters.sh b/tests/g5k/test-counters.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f83b73efcc1d70d6b941ae481688d92dd943844d
--- /dev/null
+++ b/tests/g5k/test-counters.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo testing counters sensor
+hostname
diff --git a/tests/g5k/test-infiniband.sh b/tests/g5k/test-infiniband.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f3b0f869dd87feb9ab1f9816055fdba46478aac8
--- /dev/null
+++ b/tests/g5k/test-infiniband.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo testing infiniband sensor
+hostname
diff --git a/tests/g5k/test-load.sh b/tests/g5k/test-load.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3ee05319641f0fee8116b15f2735d84b9936b583
--- /dev/null
+++ b/tests/g5k/test-load.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo testing load sensor
+hostname
diff --git a/tests/g5k/test-network.sh b/tests/g5k/test-network.sh
new file mode 100755
index 0000000000000000000000000000000000000000..74c1aec1f884b159f44cd4791aece482c66c4719
--- /dev/null
+++ b/tests/g5k/test-network.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo testing network sensor
+hostname
diff --git a/tests/g5k/test-nvidia.sh b/tests/g5k/test-nvidia.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b8d3ab4a3c7bc13ba7a663aeb87969f43ad35f11
--- /dev/null
+++ b/tests/g5k/test-nvidia.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo testing nvidia gpu sensor
+hostname
diff --git a/tests/g5k/test-rapl.sh b/tests/g5k/test-rapl.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a9fb35eaed3a85e33c775c93873bd262f37b574e
--- /dev/null
+++ b/tests/g5k/test-rapl.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo testing rapl sensor
+hostname
diff --git a/tests/g5k/test-temp.sh b/tests/g5k/test-temp.sh
new file mode 100755
index 0000000000000000000000000000000000000000..043e3d182e5953facda267850b99f57fe8b6e820
--- /dev/null
+++ b/tests/g5k/test-temp.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo testing temperature sensor
+hostname