// version 20230126
// public domain
// djb

// planned long-term cleanup:
// once all systems support getrandom() via sys/random.h,
// get rid of the other options and the SIGSYS handling

#include <unistd.h>

// automatic-alternatives 5
#ifdef getrandom_wrapper_5

#include "getrandom_wrapper.h"

int getrandom_wrapper_ready(void)
{
  return 0;
}

void getrandom_wrapper(void *x,long long xbytes)
{
  for (;;) pause();
}

#else

#include <errno.h>
#include <signal.h>

#ifdef getrandom_wrapper_1
#include <sys/random.h>
#endif

#ifdef getrandom_wrapper_2
#include <linux/random.h>
#endif

#ifdef getrandom_wrapper_3
#include <syscall.h>
#define getrandom(a,b,c) syscall(SYS_getrandom,a,b,c)
#endif

#ifdef getrandom_wrapper_4
#include <syscall.h>
#define getrandom(a,b,c) syscall(__NR_getrandom,a,b,c)
#endif

#include "getrandom_wrapper.h"

static int getrandom_wrapper_ready_core(void)
{
  char ch;
  int r;

  for (;;) {
    errno = 0;
    r = getrandom(&ch,1,0);
    if (r == 1) return 1;
    if (r == -1 && errno == ENOSYS) return 0;
    if (r == -1 && errno == EPERM) return 0; // QNAP bug
    if (r == -1 && errno == EINVAL) return 0;
  }
}

int getrandom_wrapper_ready(void)
{
  struct sigaction old_sigsys;
  int result;

  sigaction(SIGSYS,0,&old_sigsys);
  signal(SIGSYS,SIG_IGN);
  result = getrandom_wrapper_ready_core();
  sigaction(SIGSYS,&old_sigsys,0);
  return result;
}

void getrandom_wrapper(void *x,long long xbytes)
{
  while (xbytes > 0) {
    int todo = 1048576;
    if (xbytes < 1048576) todo = xbytes;

    todo = getrandom(x,todo,0);
    if (todo < 1) {
      sleep(1);
      continue;
    }

    x += todo;
    xbytes -= todo;
  }
}

#endif
