/*
 * Copyright (c) 2004, 2005 Christophe Varoqui
 */
#include <stdio.h>

#include "checkers.h"
#include "memory.h"
#include "vector.h"
#include "util.h"
#include "debug.h"
#include "structs.h"
#include "config.h"
#include "blacklist.h"

extern int
store_ble (vector blist, char * str, int origin)
{
	struct blentry * ble;

	if (!str)
		return 0;

	if (!blist)
		goto out;

	ble = MALLOC(sizeof(struct blentry));

	if (!ble)
		goto out;

	if (regcomp(&ble->regex, str, REG_EXTENDED|REG_NOSUB))
		goto out1;

	if (!vector_alloc_slot(blist))
		goto out1;

	ble->str = str;
	ble->origin = origin;
	vector_set_slot(blist, ble);
	return 0;
out1:
	FREE(ble);
out:
	FREE(str);
	return 1;
}


extern int
alloc_ble_device (vector blist)
{
	struct blentry_device * ble = MALLOC(sizeof(struct blentry_device));

	if (!ble)
		return 1;

	if (!blist || !vector_alloc_slot(blist)) {
		FREE(ble);
		return 1;
	}
	vector_set_slot(blist, ble);
	return 0;
}

extern int
set_ble_device (vector blist, char * vendor, char * product, int origin)
{
	struct blentry_device * ble;

	if (!blist)
		return 1;

	ble = VECTOR_LAST_SLOT(blist);

	if (!ble)
		return 1;

	if (vendor) {
		if (regcomp(&ble->vendor_reg, vendor,
			    REG_EXTENDED|REG_NOSUB)) {
			FREE(vendor);
			return 1;
		}
		ble->vendor = vendor;
	}
	if (product) {
		if (regcomp(&ble->product_reg, product,
			    REG_EXTENDED|REG_NOSUB)) {
			FREE(product);
			return 1;
		}
		ble->product = product;
	}
	ble->origin = origin;
	return 0;
}

int
setup_default_blist (struct config * conf)
{
	struct blentry * ble;
	struct hwentry *hwe;
	char * str;
	int i;

	str = STRDUP("^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*");
	if (!str)
		return 1;
	if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
		return 1;

	str = STRDUP("^hd[a-z]");
	if (!str)
		return 1;
	if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
		return 1;

	str = STRDUP("^dcssblk[0-9]*");
	if (!str)
		return 1;
	if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
		return 1;

	vector_foreach_slot (conf->hwtable, hwe, i) {
		if (hwe->bl_product) {
			if (alloc_ble_device(conf->blist_device))
				return 1;
			ble = VECTOR_SLOT(conf->blist_device,
					  VECTOR_SIZE(conf->blist_device) -1);
			if (set_ble_device(conf->blist_device,
					   STRDUP(hwe->vendor),
					   STRDUP(hwe->bl_product),
					   ORIGIN_DEFAULT)) {
				FREE(ble);
				return 1;
			}
		}
	}
	return 0;
}

int
_blacklist_exceptions (vector elist, char * str)
{
	int i;
	struct blentry * ele;

	vector_foreach_slot (elist, ele, i) {
		if (!regexec(&ele->regex, str, 0, NULL, 0))
			return 1;
	}
	return 0;
}

int
_blacklist (vector blist, char * str)
{
	int i;
	struct blentry * ble;

	vector_foreach_slot (blist, ble, i) {
		if (!regexec(&ble->regex, str, 0, NULL, 0))
			return 1;
	}
	return 0;
}

int
_blacklist_exceptions_device(vector elist, char * vendor, char * product)
{
	int i;
	struct blentry_device * ble;

	vector_foreach_slot (elist, ble, i) {
		if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) &&
		    !regexec(&ble->product_reg, product, 0, NULL, 0))
			return 1;
	}
	return 0;
}

int
_blacklist_device (vector blist, char * vendor, char * product)
{
	int i;
	struct blentry_device * ble;

	vector_foreach_slot (blist, ble, i) {
		if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) &&
		    !regexec(&ble->product_reg, product, 0, NULL, 0))
			return 1;
	}
	return 0;
}

#define LOG_BLIST(M) \
	if (vendor && product)						 \
		condlog(3, "%s: (%s:%s) %s", dev, vendor, product, (M)); \
	else if (wwid)							 \
		condlog(3, "%s: (%s) %s", dev, wwid, (M));		 \
	else								 \
		condlog(3, "%s: %s", dev, (M))

void
log_filter (char *dev, char *vendor, char *product, char *wwid, int r)
{
	/*
	 * Try to sort from most likely to least.
	 */
	switch (r) {
	case MATCH_NOTHING:
		break;
	case MATCH_DEVICE_BLIST:
		LOG_BLIST("vendor/product blacklisted");
		break;
	case MATCH_WWID_BLIST:
		LOG_BLIST("wwid blacklisted");
		break;
	case MATCH_DEVNODE_BLIST:
		LOG_BLIST("device node name blacklisted");
		break;
	case MATCH_DEVICE_BLIST_EXCEPT:
		LOG_BLIST("vendor/product whitelisted");
		break;
	case MATCH_WWID_BLIST_EXCEPT:
		LOG_BLIST("wwid whitelisted");
		break;
	case MATCH_DEVNODE_BLIST_EXCEPT:
		LOG_BLIST("device node name whitelisted");
		break;
	}
}

int
_filter_device (vector blist, vector elist, char * vendor, char * product)
{
	if (!vendor || !product)
		return 0;
	if (_blacklist_exceptions_device(elist, vendor, product))
		return MATCH_DEVICE_BLIST_EXCEPT;
	if (_blacklist_device(blist, vendor, product))
		return MATCH_DEVICE_BLIST;
	return 0;
}

int
filter_device (vector blist, vector elist, char * vendor, char * product)
{
	int r = _filter_device(blist, elist, vendor, product);
	log_filter(NULL, vendor, product, NULL, r);
	return r;
}

int
_filter_devnode (vector blist, vector elist, char * dev)
{
	if (!dev)
		return 0;
	if (_blacklist_exceptions(elist, dev))
		return MATCH_DEVNODE_BLIST_EXCEPT;
	if (_blacklist(blist, dev))
		return MATCH_DEVNODE_BLIST;
	return 0;
}

int
filter_devnode (vector blist, vector elist, char * dev)
{
	int r = _filter_devnode(blist, elist, dev);
	log_filter(dev, NULL, NULL, NULL, r);
	return r;
}

int
_filter_wwid (vector blist, vector elist, char * wwid)
{
	if (!wwid)
		return 0;
	if (_blacklist_exceptions(elist, wwid))
		return MATCH_WWID_BLIST_EXCEPT;
	if (_blacklist(blist, wwid))
		return MATCH_WWID_BLIST;
	return 0;
}

int
filter_wwid (vector blist, vector elist, char * wwid)
{
	int r = _filter_wwid(blist, elist, wwid);
	log_filter(NULL, NULL, NULL, wwid, r);
	return r;
}

int
_filter_path (struct config * conf, struct path * pp)
{
	int r;

	r = _filter_devnode(conf->blist_devnode, conf->elist_devnode,pp->dev);
	if (r > 0)
		return r;
	r = _filter_device(conf->blist_device, conf->elist_device,
			   pp->vendor_id, pp->product_id);
	if (r > 0)
		return r;
	r = _filter_wwid(conf->blist_wwid, conf->elist_wwid, pp->wwid);
	return r;
}

int
filter_path (struct config * conf, struct path * pp)
{
	int r=_filter_path(conf, pp);
	log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, r);
	return r;
}

void
free_blacklist (vector blist)
{
	struct blentry * ble;
	int i;

	if (!blist)
		return;

	vector_foreach_slot (blist, ble, i) {
		if (ble) {
			regfree(&ble->regex);
			FREE(ble->str);
			FREE(ble);
		}
	}
	vector_free(blist);
}

void
free_blacklist_device (vector blist)
{
	struct blentry_device * ble;
	int i;

	if (!blist)
		return;

	vector_foreach_slot (blist, ble, i) {
		if (ble) {
			if (ble->vendor) {
				regfree(&ble->vendor_reg);
				FREE(ble->vendor);
			}
			if (ble->product) {
				regfree(&ble->product_reg);
				FREE(ble->product);
			}
			FREE(ble);
		}
	}
	vector_free(blist);
}
