#!/bin/bash
###SHELLPACK preamble seeker-install 0
WEB_LOCATION="http://www.linuxinsight.com/files/seeker.c"
MIRROR_LOCATION="$WEBROOT/seeker/seeker.c"

###SHELLPACK parseargBegin
###SHELLPACK parseargEnd

cd $SHELLPACK_SOURCES || die Sources directory does not exist
rm -rf seeker-${VERSION}-installed
mkdir -p seeker-${VERSION}-installed

sources_fetch $WEB_LOCATION $MIRROR_LOCATION $SHELLPACK_SOURCES/seeker-${VERSION}-installed/seeker.c
cd $SHELLPACK_SOURCES/seeker-${VERSION}-installed

# Patch benchmark to make it a ro/rw IO benchmark
TEMPFILE=`mktemp`
LINECOUNT=`wc -l $0 | awk '{print $1}'`
PATCHSTART=`grep -n "BEGIN PATCH FILE" $0 | tail -1 | awk -F : '{print $1}'`
tail -$(($LINECOUNT-$PATCHSTART)) $0 | grep -v "^###" > $TEMPFILE
cat $TEMPFILE | patch -p0 || exit $SHELLPACK_FAILURE
rm $TEMPFILE

gcc ${MMTESTS_BUILD_CFLAGS:--O2} seeker.c -o seeker
if [ $? -ne 0 ]; then
	die "$P: Failed to build seeker"
fi

exit $SHELLPACK_SUCCESS

==== BEGIN PATCH FILE ====
--- seeker.c.orig	2014-10-03 13:27:38.467169828 +0100
+++ seeker.c	2014-10-03 13:32:14.067951111 +0100
@@ -1,6 +1,7 @@
 #define _LARGEFILE64_SOURCE
 
 #include <stdio.h>
+#include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
@@ -8,13 +9,17 @@
 #include <signal.h>
 #include <sys/fcntl.h>
 #include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysinfo.h>
 #include <linux/fs.h>
 
-#define BLOCKSIZE 512
-#define TIMEOUT 30
+#define TIMEOUT 900
+#define INTERVAL 5
 
-int count;
+unsigned int count;
 time_t start;
+int fd;
 
 void done()
 {
@@ -22,17 +27,19 @@
 
 	time(&end);
 
-	if (end < start + TIMEOUT) {
-		printf(".");
-		alarm(1);
-		return;
-	}
-
 	if (count) {
-	  printf(".\nResults: %d seeks/second, %.2f ms random access time\n",
-		 count / TIMEOUT, 1000.0 * TIMEOUT / count);
+		printf("mark: %d seeks/second, %.2f ms random access time\n",
+			count / INTERVAL, 1000.0 * INTERVAL / count);
+		count = 0;
+	}
+	if (end >= start + TIMEOUT) {
+		if (close(fd) == -1) {
+			perror("Failed to close file properly\n");
+			exit(EXIT_FAILURE);
+		}
+		exit(EXIT_SUCCESS);
 	}
-	exit(EXIT_SUCCESS);
+	alarm(INTERVAL);
 }
 
 void handle(const char *string, int error)
@@ -45,40 +52,89 @@
 
 int main(int argc, char **argv)
 {
-	char buffer[BLOCKSIZE];
-	int fd, retval;
+	int retval;
 	unsigned long numblocks;
+	int blocksize;
 	off64_t offset;
+	int open_flags = O_RDONLY;
+	int rw = 0;
+	char *buffer;
+	int page_mask = getpagesize() - 1;
+	int fadvise_size;
 
 	setvbuf(stdout, NULL, _IONBF, 0);
 
 	printf("Seeker v2.0, 2007-01-15, "
 	       "http://www.linuxinsight.com/how_fast_is_your_disk.html\n");
 
-	if (argc != 2) {
-		printf("Usage: seeker <raw disk device>\n");
+	if (argc != 4) {
+		printf("Usage: seeker [block|file] [read|write] <device|file>\n");
 		exit(EXIT_SUCCESS);
 	}
 
-	fd = open(argv[1], O_RDONLY);
+	if (!strcmp(argv[2], "read")) {
+		open_flags = O_RDONLY;
+		rw = 0;
+	} else if (!strcmp(argv[2], "write")) {
+		open_flags = O_WRONLY;
+		rw = 1;
+	} else {
+		printf("Unable to parse read/write parameter\n");
+		exit(EXIT_FAILURE);
+	}
+
+	fd = open(argv[3], open_flags);
 	handle("open", fd < 0);
 
-	retval = ioctl(fd, BLKGETSIZE, &numblocks);
-	handle("ioctl", retval == -1);
-	printf("Benchmarking %s [%luMB], wait %d seconds",
-	       argv[1], numblocks / 2048, TIMEOUT);
+	if (!strcmp(argv[1], "block")) {
+		retval= ioctl(fd, BLKGETSIZE, &numblocks);
+		handle("ioctl-BLKGETSIZE", retval== -1);
+
+		retval= ioctl(fd, BLKSSZGET, &blocksize);
+		handle("ioctl-BLKSSZGET", retval== -1);
+	} else {
+		struct stat sb;
+		retval= fstat(fd, &sb);
+		handle("fstat", retval== -1);
+		numblocks = sb.st_size;
+		blocksize = sb.st_blksize;
+	}
+	fadvise_size = (blocksize + getpagesize() - 1) & ~page_mask;
+
+	buffer = malloc(blocksize);
+	if (!buffer) {
+		printf("Malloc failed of buffer size %d\n", blocksize);
+		exit(EXIT_FAILURE);
+	}
+
+	printf("Benchmarking %s %s %s [%luMB] blocks %lu blksize %d fadvise_size %d, duration %d seconds\n",
+		argv[1], argv[2], argv[3],
+		numblocks * blocksize / 1048576,
+		numblocks, blocksize, fadvise_size,
+		TIMEOUT);
 
 	time(&start);
 	srand(start);
 	signal(SIGALRM, &done);
-	alarm(1);
+	alarm(INTERVAL);
 
 	for (;;) {
-		offset = (off64_t) numblocks * random() / RAND_MAX;
-		retval = lseek64(fd, BLOCKSIZE * offset, SEEK_SET);
+		double rand_range = (double)rand() / RAND_MAX;
+		offset = (off64_t)(rand_range * numblocks);
+		retval= lseek64(fd, offset, SEEK_SET);
 		handle("lseek64", retval == (off64_t) -1);
-		retval = read(fd, buffer, BLOCKSIZE);
-		handle("read", retval < 0);
+
+		if (rw == 0) {
+			posix_fadvise(fd, offset & ~page_mask, fadvise_size, POSIX_FADV_DONTNEED);
+			retval= read(fd, buffer, blocksize);
+			handle("read", retval < 0);
+		} else {
+			retval = write(fd, buffer, blocksize);
+			handle("write", retval < 0);
+			sync_file_range(fd, offset & ~page_mask, fadvise_size, SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER);
+		}
+		if (retval!= blocksize)
+			printf("Truncated IO count %u remain %d\n", count, blocksize - retval);
 		count++;
 	}
 	/* notreached */
