/*
  Copyright (C) 2000 Jacob Chr. Poulsen.  All rights reserved.
  
  This file may be distributed under the terms of the GNU Public License.

  Block a num of meg ram in memory. 
*/

#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <sys/time.h>
#include <sys/resource.h>


//! max size to alloc in one process. 
size_t maxBlockSize=1800; 
//! 
size_t bytesPrMb=1024*1024;
//! size to block in Mb
size_t sizeToBlock=0;
//! 
size_t maxRlimitLockSize=0;
char* memMapedRegion; 

void help() {
  fprintf(stderr,"\nusage block_memory [size in Mb]\n");
  exit(1);
};

// Print a error message. 
void error(const char* format,...) {
  va_list ap;
  va_start(ap,format);
  vfprintf(stderr,format,ap);
  help();
};

//! force the pages into memory. by writing the pid into the first int of each page
void touchPages() {
  pid_t pid;
  long pagesize;
  char* p;
  long long j;
  size_t bytes=sizeToBlock*bytesPrMb;

  pid = getpid();
  pagesize = sysconf( _SC_PAGESIZE);
  
  for (j=0; j<bytes; j +=pagesize)
      (int)(memMapedRegion[j]) = pid;  
};



void blockMem(size_t bytes) {  

  // MAP_LOCKED shut prevent paging
  memMapedRegion=(char*)mmap(NULL,bytes,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON|MAP_LOCKED,0,0);
  if(memMapedRegion==MAP_FAILED) {
    error("Unable to mmap error %d - %s\n",errno,"strerror(errno)");
    exit(2);
  }
  // MAP_LOCKED dont seams to work on my linux eg caling mlock
  if(mlock(memMapedRegion,bytes-2 )==-1) {
    error("Unable to mlock region error %d - %s\n",errno,strerror(errno));
    exit(2);
  };
};


/*!
  Tryes to get the max size of a locked region. this dont seams to work on my laptop. ;( 
  where it parses the max size but fails in the mlock call.
 */
void testLimits() {
  struct rlimit r;
  if( getrlimit (RLIMIT_MEMLOCK, &r) ==0 ) {
    maxRlimitLockSize = r.rlim_cur;
  } else {
    maxRlimitLockSize = (size_t)-1;
  };
};


int main(int argc, char* argv[]) {

  if(argc<2) help();
  sizeToBlock = atol(argv[1]);

  testLimits();
  
  if(sizeToBlock<1 || sizeToBlock>maxBlockSize) {
    error("only able to block betin %d and %d Mb\n",1,maxBlockSize);
  };
  
  if((sizeToBlock*bytesPrMb) > maxRlimitLockSize ) {
    error("requestet block size (%d) larger the lock rlimit size (%d)\n",(sizeToBlock*bytesPrMb),maxRlimitLockSize);
    
  };
  if(geteuid()!=0) {
    error("only root is allowd to lock pages in memory -- please run as root");
  };
 
  printf("will block %d Mb\n",sizeToBlock);
  blockMem(sizeToBlock*bytesPrMb);
  //touchPages();

  printf("press t [to toutch the pages] or r [release]\n");
  while(1) {
    switch(getchar()) {
    case 't': 
      touchPages();
      break;
    case 'r':
      munmap(memMapedRegion,sizeToBlock*1024*1024);
      exit(0);
      break;
    case EOF:
      fprintf(stderr,"end of stdin exiting\n");
      exit(0);
    default:
      ;
    };
  };
  fprintf(stderr,"end of main ??? \n");
  return 0;
};
