// MCL_FUTURE is incompatible with V8 (crashes Isolate::Initialize because V8
// remaps heap regions); MCL_CURRENT is enough since the sandbox has no swap so
// anonymous heap pages are already pinned.
#define _GNU_SOURCE
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static int is_own_preload_token(const char *token) {
  const char *basename = strrchr(token, '/');
  basename = basename == NULL ? token : basename + 1;
  return strcmp(basename, "replicas-lockmem.so") == 0;
}

static void scrub_own_preload(void) {
  const char *current = getenv("LD_PRELOAD");
  if (current == NULL) {
    return;
  }

  char *copy = strdup(current);
  if (copy == NULL) {
    return;
  }

  char *next = copy;
  char *token;
  size_t output_len = 0;
  int removed = 0;
  char *output = malloc(strlen(current) + 1);
  if (output == NULL) {
    free(copy);
    return;
  }
  output[0] = '\0';

  while ((token = strsep(&next, ":")) != NULL) {
    if (is_own_preload_token(token)) {
      removed = 1;
      continue;
    }

    if (output_len > 0) {
      output[output_len++] = ':';
      output[output_len] = '\0';
    }
    strcpy(output + output_len, token);
    output_len += strlen(token);
  }

  if (removed) {
    if (output_len == 0) {
      unsetenv("LD_PRELOAD");
    } else {
      setenv("LD_PRELOAD", output, 1);
    }
  }

  free(output);
  free(copy);
}

__attribute__((constructor))
static void replicas_lockmem_init(void) {
  scrub_own_preload();

  if (mlockall(MCL_CURRENT) != 0) {
    fprintf(stderr, "[lockmem] mlockall(MCL_CURRENT) failed: %s (errno=%d); engine running unprotected\n",
            strerror(errno), errno);
    return;
  }

  if (getenv("REPLICAS_LOCKMEM_VERBOSE") != NULL) {
    fprintf(stderr, "[lockmem] engine pages locked (pid=%d)\n", getpid());
  }
}
