/*-
 * Copyright (c) 2011, 2012, 2013, 2016 Spectra Logic Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification.
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
 *    substantially similar to the "NO WARRANTY" disclaimer below
 *    ("Disclaimer") and any redistribution must be conditioned upon
 *    including a substantially similar Disclaimer requirement for further
 *    binary redistribution.
 *
 * NO WARRANTY
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * Authors: Justin T. Gibbs     (Spectra Logic Corporation)
 *
 * $FreeBSD: releng/12.4/lib/libdevdctl/event.h 301001 2016-05-30 19:25:00Z pfg $
 */

/**
 * \file devdctl_event.h
 *
 * \brief Class hierarchy used to express events received via
 *        the devdctl API.
 */

#ifndef _DEVDCTL_EVENT_H_
#define	_DEVDCTL_EVENT_H_

/*============================ Namespace Control =============================*/
namespace DevdCtl
{

/*=========================== Forward Declarations ===========================*/
class EventFactory;

/*============================= Class Definitions ============================*/
/*-------------------------------- NVPairMap ---------------------------------*/
/**
 * NVPairMap is a specialization of the standard map STL container.
 */
typedef std::map<std::string, std::string> NVPairMap;

/*----------------------------------- Event ----------------------------------*/
/**
 * \brief Container for the name => value pairs that comprise the content of
 *        a device control event.
 *
 * All name => value data for events can be accessed via the Contains()
 * and Value() methods.  name => value pairs for data not explicitly
 * received as a name => value pair are synthesized during parsing.  For
 * example, ATTACH and DETACH events have "device-name" and "parent"
 * name => value pairs added.
 */
class Event
{
	friend class EventFactory;

public:
	/** Event type */
	enum Type {
		/** Generic event notification. */
		NOTIFY  = '!',

		/** A driver was not found for this device. */
		NOMATCH = '?',

		/** A bus device instance has been added. */
		ATTACH  = '+',

		/** A bus device instance has been removed. */
		DETACH  = '-'
	};

	/**
	 * Factory method type to construct an Event given
	 * the type of event and an NVPairMap populated from
	 * the event string received from devd.
	 */
	typedef Event* (BuildMethod)(Type, NVPairMap &, const std::string &);

	/** Generic Event object factory. */
	static BuildMethod Builder;

	static Event *CreateEvent(const EventFactory &factory,
				  const std::string &eventString);

	/**
	 * Returns the devname, if any, associated with the event
	 *
	 * \param name	Devname, returned by reference
	 * \return	True iff the event contained a devname
	 */
	virtual bool DevName(std::string &name)	const;

	/**
	 * Returns the absolute pathname of the device associated with this
	 * event.
	 *
	 * \param name	Devname, returned by reference
	 * \return	True iff the event contained a devname
	 */
	bool DevPath(std::string &path)		const;

	/**
	 * Returns true iff this event refers to a disk device
	 */
	bool IsDiskDev()			const;

	/** Returns the physical path of the device, if any
	 *
	 * \param path	Physical path, returned by reference
	 * \return	True iff the event contains a device with a physical
	 * 		path
	 */
	bool PhysicalPath(std::string &path)	const;

	/**
	 * Provide a user friendly string representation of an
	 * event type.
	 *
	 * \param type  The type of event to map to a string.
	 *
	 * \return  A user friendly string representing the input type.
	 */
	static const char  *TypeToString(Type type);

	/**
	 * Determine the availability of a name => value pair by name.
	 *
	 * \param name  The key name to search for in this event instance.
	 *
	 * \return  true if the specified key is available in this
	 *          event, otherwise false.
	 */
	bool Contains(const std::string &name)		 const;

	/**
	 * \param key  The name of the key for which to retrieve its
	 *             associated value.
	 *
	 * \return  A const reference to the string representing the
	 *          value associated with key.
	 *
	 * \note  For key's with no registered value, the empty string
	 *        is returned.
	 */
	const std::string &Value(const std::string &key) const;

	/**
	 * Get the type of this event instance.
	 *
	 * \return  The type of this event instance.
	 */
	Type GetType()					 const;

	/**
	 * Get the original DevdCtl event string for this event.
	 *
	 * \return  The DevdCtl event string.
	 */
	const std::string &GetEventString()		 const;

	/**
	 * Convert the event instance into a string suitable for
	 * printing to the console or emitting to syslog.
	 *
	 * \return  A string of formatted event data.
	 */
	std::string ToString()				 const;

	/**
	 * Pretty-print this event instance to cout.
	 */
	void Print()					 const;

	/**
	 * Pretty-print this event instance to syslog.
	 *
	 * \param priority  The logging priority/facility.
	 *                  See syslog(3).
	 */
	void Log(int priority)				 const;

	/**
	 * Create and return a fully independent clone
	 * of this event.
	 */
	virtual Event *DeepCopy()			 const;

	/** Destructor */
	virtual ~Event();

	/**
	 * Interpret and perform any actions necessary to
	 * consume the event.
	 *
	 * \return True if this event should be queued for later reevaluation
	 */
	virtual bool Process()				 const;

	/**
	 * Get the time that the event was created
	 */
	timeval GetTimestamp()				 const;

	/**
	 * Add a timestamp to the event string, if one does not already exist
	 * TODO: make this an instance method that operates on the std::map
	 * instead of the string.  We must fix zfsd's CaseFile serialization
	 * routines first, so that they don't need the raw event string.
	 *
	 * \param[in,out] eventString The devd event string to modify
	 */
	static void TimestampEventString(std::string &eventString);

	/**
	 * Access all parsed key => value pairs.
	 */
	const NVPairMap &GetMap()			 const;

protected:
	/** Table entries used to map a type to a user friendly string. */
	struct EventTypeRecord
	{
		Type         m_type;
		const char  *m_typeName;
	};

	/**
	 * Constructor
	 *
	 * \param type  The type of event to create.
	 */
	Event(Type type, NVPairMap &map, const std::string &eventString);

	/** Deep copy constructor. */
	Event(const Event &src);

	/** Always empty string returned when NVPairMap lookups fail. */
	static const std::string    s_theEmptyString;

	/** Unsorted table of event types. */
	static EventTypeRecord      s_typeTable[];

	/** The type of this event. */
	const Type                  m_type;

	/**
	 * Event attribute storage.
	 *
	 * \note Although stored by reference (since m_nvPairs can
	 *       never be NULL), the NVPairMap referenced by this field
	 *       is dynamically allocated and owned by this event object.
	 *       m_nvPairs must be deleted at event destruction.
	 */
	NVPairMap                  &m_nvPairs;

	/**
	 * The unaltered event string, as received from devd, used to
	 * create this event object.
	 */
	std::string                 m_eventString;

private:
	/**
	 * Ingest event data from the supplied string.
	 *
	 * \param[in] eventString  The string of devd event data to parse.
	 * \param[out] nvpairs     Returns the parsed data
	 */
	static void ParseEventString(Type type, const std::string &eventString,
				     NVPairMap &nvpairs);
};

inline Event::Type
Event::GetType() const
{
	return (m_type);
}

inline const std::string &
Event::GetEventString() const
{
	return (m_eventString);
}

inline const NVPairMap &
Event::GetMap()	const
{
	return (m_nvPairs);
}

/*--------------------------------- EventList --------------------------------*/
/**
 * EventList is a specialization of the standard list STL container.
 */
typedef std::list<Event *> EventList;

/*-------------------------------- DevfsEvent --------------------------------*/
class DevfsEvent : public Event
{
public:
	/** Specialized Event object factory for Devfs events. */
	static BuildMethod Builder;

	virtual Event *DeepCopy()		const;

	/**
	 * Interpret and perform any actions necessary to
	 * consume the event.
	 * \return True if this event should be queued for later reevaluation
	 */
	virtual bool Process()			const;

	bool IsWholeDev()			const;
	virtual bool DevName(std::string &name)	const;

protected:
	/**
	 * Given the device name of a disk, determine if the device
	 * represents the whole device, not just a partition.
	 *
	 * \param devName  Device name of disk device to test.
	 *
	 * \return  True if the device name represents the whole device.
	 *          Otherwise false.
	 */
	static bool IsWholeDev(const std::string &devName);

	/** DeepCopy Constructor. */
	DevfsEvent(const DevfsEvent &src);

	/** Constructor */
	DevfsEvent(Type, NVPairMap &, const std::string &);
};

/*--------------------------------- GeomEvent --------------------------------*/
class GeomEvent : public Event
{
public:
	/** Specialized Event object factory for GEOM events. */
	static BuildMethod Builder;

	virtual Event *DeepCopy()	const;

	virtual bool DevName(std::string &name)	const;

	const std::string &DeviceName()	const;

protected:
	/** Constructor */
	GeomEvent(Type, NVPairMap &, const std::string &);

	/** Deep copy constructor. */
	GeomEvent(const GeomEvent &src);

	std::string m_devname;
};

/*--------------------------------- ZfsEvent ---------------------------------*/
class ZfsEvent : public Event
{
public:
	/** Specialized Event object factory for ZFS events. */
	static BuildMethod Builder;

	virtual Event *DeepCopy()	const;

	virtual bool DevName(std::string &name)	const;

	const std::string &PoolName()	const;
	Guid		   PoolGUID()	const;
	Guid		   VdevGUID()	const;

protected:
	/** Constructor */
	ZfsEvent(Type, NVPairMap &, const std::string &);

	/** Deep copy constructor. */
	ZfsEvent(const ZfsEvent &src);

	Guid	m_poolGUID;
	Guid	m_vdevGUID;
};

//- ZfsEvent Inline Public Methods --------------------------------------------
inline const std::string&
ZfsEvent::PoolName() const
{
	/* The pool name is reported as the subsystem of ZFS events. */
	return (Value("subsystem"));
}

inline Guid
ZfsEvent::PoolGUID() const
{
	return (m_poolGUID);
}

inline Guid
ZfsEvent::VdevGUID() const
{
	return (m_vdevGUID);
}

} // namespace DevdCtl
#endif /*_DEVDCTL_EVENT_H_ */
