/*
   drbdadm_minor_table.c

   This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
   It was written by Johannes Thoma <johannes.thoma@linbit.com>

   Copyright (C) 2002-2008, LINBIT Information Technologies GmbH.
   Copyright (C) 2002-2008, Philipp Reisner <philipp.reisner@linbit.com>.
   Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.

   drbd is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   drbd is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with drbd; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

 */

/* This keeps track of which DRBD minor was configured in which
 * config file. This is required to have alternative config files
 * (-c switch) and userland event handlers.
 */


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

#include "config.h"

#define MAX_MINOR 256
#define MAX_REGISTER_PATH_LEN	1024

/* buf has to be big enough to hold that path.
 * it is assumed that sprintf cannot fail :-] */
void linkname_from_minor(char *buf, int minor)
{
	sprintf(buf, "%s/drbd-minor-%d.conf", DRBD_LIB_DIR, minor);
}

int unregister_minor(int minor)
{
	char buf[255];

	if (minor >= MAX_MINOR || minor < 0) {
		fprintf(stderr, "unregister_minor: minor too big (%d).\n", minor);
		return -1;
	}

	linkname_from_minor(buf, minor);
	if (unlink(buf) < 0) {
		if (errno != ENOENT) {
			perror("unlink");
			return -1;
		}
	}
	return 0;
}

int register_minor(int minor, const char *path)
{
	char buf[255];
	struct stat stat_buf;
	int err = -1;

	if (minor >= MAX_MINOR || minor < 0) {
		fprintf(stderr, "register_minor: minor too big (%d).\n", minor);
		return -1;
	}

	linkname_from_minor(buf, minor);

	if (!path || !path[0])
		fprintf(stderr, "Cannot register an empty path.\n");
	else if (path[0] != '/')
		fprintf(stderr, "Absolute path expected, "
			"won't register relative path (%s).\n", path);
	else if (strlen(path) >= MAX_REGISTER_PATH_LEN)
		fprintf(stderr, "path (%s):\ntoo long to be registered, "
				"max path len supported: %u\n",
				path, MAX_REGISTER_PATH_LEN-1);
	else if (stat(path, &stat_buf) < 0)
		fprintf(stderr, "stat(%s): %m\n", path);
	else if (unlink(buf) < 0 && errno != ENOENT)
		fprintf(stderr, "unlink(%s): %m\n", buf);
	else if (symlink(path, buf) < 0)
		fprintf(stderr, "symlink(%s, %s): %m\n", path, buf);
	else
		/* it did work out after all! */
		err = 0;

	return err;
}

/* This returns a static buffer containing the real
 * configuration file known to be used last for this minor.
 * If you need the return value longer, stuff it away with strdup. */
char *lookup_minor(int minor)
{
	static char buf[255];
	static char resolved_path[MAX_REGISTER_PATH_LEN+1];
	struct stat stat_buf;
	ssize_t len;

	if (minor >= MAX_MINOR || minor < 0) {
		fprintf(stderr, "register_minor: minor too big (%d).\n", minor);
		return NULL;
	}

	linkname_from_minor(buf, minor);

	if (stat(buf, &stat_buf) < 0) {
		if (errno != ENOENT)
			fprintf(stderr, "stat(%s): %m\n", buf);
		return NULL;
	}

	len = readlink(buf, resolved_path, sizeof(resolved_path)-1);
	if (len < 0) {
		perror("readlink");
		return NULL;
	}
	if (len >= MAX_REGISTER_PATH_LEN)
		fprintf(stderr, "readlink(%s): result has probably been truncated\n",
				buf);

	resolved_path[len] = '\0';
	return resolved_path;
}


#ifdef TEST

int main(int argc, char ** argv)
{
	register_minor(1, "/etc/drbd-xy.conf");
	register_minor(15, "/etc/drbd-82.conf");
	register_minor(14, "/../../../../../../etc/drbd-82.conf");
	printf("Minor 1 is %s.\n", lookup_minor(1));
	printf("Minor 2 is %s.\n", lookup_minor(2));
	printf("Minor 14 is %s.\n", lookup_minor(14));
	printf("Minor 15 is %s.\n", lookup_minor(15));
	return 0;
}

#endif
