#include <sys/types.h>
#include <sys/uio.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char **argv)
{
	const char *me = "rip-vm";
	struct iovec local_iov, remote_iov;
	unsigned long begin = 0, end = 0;
	char *eptr, *outfn = "rip-vm.bin";
	uint8_t *buf;
	int fd, ret = EXIT_FAILURE;
	long the_pid;
	ssize_t sz;
	pid_t pid;

	switch(argc) {
	case 5:
		outfn = argv[4];
	case 4:
		end = strtoul(argv[3], &eptr, 0);
		if ( *eptr || (end == ULONG_MAX && errno) )
			goto err_usage;
	case 3:
		begin = strtoul(argv[2], &eptr, 0);
		if ( *eptr || (begin == ULONG_MAX && errno) )
			goto err_usage;
	case 2:
		the_pid = strtol(argv[1], &eptr, 0);
		if ( *eptr || (the_pid == LONG_MAX && errno) )
			goto err_usage;
		if ( the_pid > INT_MAX || the_pid < 0)
			goto err_usage;
		pid = the_pid;
	case 1:
		me = argv[0];
		break;
	default:
		goto err_usage;
	}

	if (argc < 4 || end < begin)
		goto err_usage;

	printf("%s: About to rip: pid=%u begin=%p end=%p\n",
		me, pid, (void *)begin, (void *)end);
	printf("%s: writing it to %s\n", me, outfn);

	buf = malloc(end - begin);
	if ( NULL == buf ) {
		fprintf(stderr, "%s: malloc: %s\n", me, strerror(errno));
		goto out;
	}

	fd = open(outfn, O_WRONLY|O_CREAT|O_TRUNC, 0640);
	if ( fd < 0 ) {
		fprintf(stderr, "%s: open: %s: %s\n",
			me, outfn, strerror(errno));
		goto out_free;
	}

	local_iov.iov_base = buf;
	local_iov.iov_len = end - begin;
	remote_iov.iov_base = (void *)begin;
	remote_iov.iov_len = end - begin;
	sz = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0);
	if ( sz < 0 ) {
		fprintf(stderr, "%s: process_vm_readv: %s\n",
				me, strerror(errno));
		goto out_close;
	}

	printf("Got %zd bytes\n", sz);

	sz = write(fd, buf, sz);
	if ( sz < 0 ) {
		fprintf(stderr, "%s: write: %s: %s\n",
			me, outfn, strerror(errno));
		goto out_close;
	}

	ret = EXIT_SUCCESS;

out_close:
	close(fd);
out_free:
	free(buf);
out:
	return ret;

err_usage:
	fprintf(stderr, "%s: Usage:\n\n", me);
	fprintf(stderr, "\tDumps virtual memory out of a running process\n");
	fprintf(stderr, "\n\t%s pid begin end\n", me);
	return EXIT_FAILURE;
}
