// -*- c++ -*-
//------------------------------------------------------------------------------
//                            SigAction.h
//------------------------------------------------------------------------------
//  Copyright (c) 1997 by Vladislav Grinchenko
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//------------------------------------------------------------------------------

#ifndef _SigAction_h
#define _SigAction_h

// System includes
//
#include <signal.h>
#include <errno.h>

#include "assa/Assert.h"
#include "assa/SigSet.h"

// some convenient typedefs
//
extern "C" {
typedef struct sigaction SIGACTION;
typedef void (*C_SIG_HANDLER)( int );
}

namespace ASSA {

/** @file SigAction.h

SigAction is a C++ wrapper around sigaction structure

Class SigAction implements a C++ wrapper around
struct sigaction. It class also provides a range of all possible 
operation that can be performed on it, including sigaction(2) system call.

struct sigaction is defined as:

<pre>
   struct sigaction {
       void      (*sa_handler) ();
       sigset_t  sa_mask;
       int       sa_flags;
       void      (*sa_sigaction) (int, siginfo_t*, void*);
   };
</pre>

It is used to set all the details of what your process should do when
a signal arrives. It encapsulates the action to be taken on receipt
of a particular signal.

The most important member is sa_handler, which takes a pointer
to a function. This function will be invoked whenever the process gets
a particular POSIX.1 signal.

Some of the member function of SigAction class take a parameter
of type C_SIG_HANDLER. 

It can be one of:
   - "C" signal handling function (sa_handler above):\n
      void sig_handler (int signum)
   - SIG_ERR 
   - SIG DFL takes default signal action when caught
   - SIG_IGN ignores signal (for those that can be ignored)

The sa_mask mask for the signal action specifies a set of signals 
to be blocked while the signal handler is active. 
On the entry into the signal handler,
that set of signals is added to a set of signals already being
blocked from delivery when the signal is delivered to the process. 
In addition, the signal that caused the handler to be executed will also
be blocked (SIGSTOP and SIGKILL cannot be blocked - this is enforced 
by the underlying OS).

Flags sa_flags in the set of flags ORed that allows to modify 
the delivery of the signal. 
POSIX.1 spec. defines only SA_NOCLDSTOP flag. All other
flags are system-dependent. Consult your local sigaction(2)
man page for details.

Because SigAction is a wrapper around sigaction(2),
after sig_handler returns (and before anything else), the OS
will reinstall current disposition for the signal. To reset signal's 
disposition to SIG_DFL, SUN Solaris OS uses flag SA_RESETHAND, and Linux OS
uses SA_ONESHOT.
*/

class SigAction
{
public:
	/** Default constructor creates SigAction object
	    with null-action.
	*/
	SigAction();

	/** Construct a SigAction object with "C" signal
	    handler function. This constructor doesn't install any
	    actions - it is merely a shell for actiono to be installed
	    for any signal(s). Thus, you can reuse the same object for
	    number of differen signals.

	    @param handler_ "C" signal handler function to call.
	    @param sig_mask_ Set of signals to block while handler_
	    is active.
	    @param flags_ Controls behavior of signal handler (OS-specific:
	    see Available Options: section of documentation).
	*/
	SigAction (C_SIG_HANDLER handler_, 
			   SigSet*       sig_mask_ = 0, 
			   int           flags_ = 0);

	/** Construct a SigAction with "C" signal handler function
	    and change program's disposition for signum_
	    immediately.<p>
	    First argument is the "C" function. It cannot be a non-static
	    C++ class member function. This function pretty much simulates
	    C-like approach the the signal handling. For C++ member 
	    function approach, see SigHandler & Co.

	    @param handler_ "C" signal handler function to call.
	    @param signum_  Signal which disposition is to change.
	    @param sig_mask_ Set of signals to block while handler_
	    is active.
	    @param flags_ Controls behavior of signal handler (OS-specific:
	    see Available Options: section of documentation).
	*/
	SigAction (C_SIG_HANDLER handler_,
			   int           signum_,
			   SigSet*       sig_mask_ = 0, 
			   int           flags_ = 0);

	/** Register this object as current disposition for signal
	    signum_, and store old disposition in oaction_,
	    if not NULL. This function installs C_SIG_HANDLER this
	    object represents, thus simulating C-like approach to signal
	    handling.

	    @param signum_ Signal which disposition to install.
	    @param oaction_ Placeholder for the old disposition.
	    @return 0 on success, -1 on error, with errno indicating
	    the error.
	*/
	int register_action (int signum_, SigAction* oaction_ = 0);

	/** Change object's disposition to oaction_, and install
	    it as current disposition for the signal signum_.

	    @param signum_ Signal which disposition to restore.
	    @param oaction_ Disposition to restore.
	    @return 0 on success, -1 on error, with errno indicating
	    the error.
	*/
	int restore_action (int signum_, SigAction& oaction_);

	/** Retrieve current disposition for the signal signum_
	    into this object.
	    @param signum_ Signal number
	    @return 0 on success, -1 on error, with errno indicating
	    the error.
	*/
	int retrieve_action (int signum_);

	/** Set sigaction structure to sa_
	    @param sa_ New value for internal struct sigaction.
	*/
	void action (SIGACTION * sa_);

	/** Retrieve current sigaction.
	    @return Pointer to an internal struct sigaction.
	*/
	SIGACTION * action ();

	/** Set signal flags to new_flags_.
	    @param new_flags_ New flags for this action.
	*/
	void flags (int new_flags_);

	/** Retrieve current flags.
	    @return Value of current flags for this action.
	*/
	int flags ();

	/** Set new signal mask mask_set_.
	 */
	void mask (SigSet & mask_set_);

	/** Retrieve current signal mask.
	 */
	SigSet mask ();

	/** Set new signal handler to function pointer sha_.
	 */
	void handler (C_SIG_HANDLER sha_);

	/** Retrieve current signal handler function.
	 */
	C_SIG_HANDLER handler ();

	/** Conversion operator that converts SigAction to a
	    pointer to the internal struct sigaction data 
	    member for direct use with C-library functions.
	*/
	operator SIGACTION *();
  
private: 
	/// sigaction structure itself
	SIGACTION m_sa;  
};

//-------------------------------------------------------------------------
//------------------------Inline functions---------------------------------
//-------------------------------------------------------------------------
inline
SigAction::
SigAction ()
{
	trace_with_mask("SigAction::SigAction", SIGACT);

	m_sa.sa_flags = 0;
	sigemptyset(&m_sa.sa_mask);
	*(C_SIG_HANDLER*) &m_sa.sa_handler = (C_SIG_HANDLER) 0;
}

inline
SigAction::
SigAction (C_SIG_HANDLER handler_, 
					  SigSet*       sig_mask_,
					  int           flags_)
{
	trace_with_mask("SigAction::SigAction(,,)", SIGACT);
  
	m_sa.sa_flags = flags_;
	if (sig_mask_ == NULL) {
		sigemptyset(&m_sa.sa_mask);
	}
	else {
		/*---
		  here, suppose to do bitwise structure assignment,
		  but does it really do so?
		  = *sig_mask_
		     = *(sig_mask_.operator *())
		        = *(SigSet *tmp = &sig_mask_.m_sa) ????
		---*/
		m_sa.sa_mask = **sig_mask_;
	}
	*(C_SIG_HANDLER*) &m_sa.sa_handler = (C_SIG_HANDLER) handler_;
}

inline
SigAction::
SigAction (C_SIG_HANDLER handler_,
		   int           signum_,
		   SigSet*       sig_mask_,
		   int           flags_)
{
	trace_with_mask("SigAction::SigAction(,,,)", SIGACT);
  
	m_sa.sa_flags = flags_;
	if (sig_mask_ == NULL) {
		sigemptyset(&m_sa.sa_mask);
	}
	else {
		/*---  same problem as above... ---*/
		m_sa.sa_mask = **sig_mask_;
	}
	*(C_SIG_HANDLER*) &m_sa.sa_handler = (C_SIG_HANDLER) handler_;
	
	/*--- installing disposition... ---*/
	sigaction (signum_, &m_sa, 0);
}

inline void
SigAction::
action (SIGACTION* sa_)
{
	trace_with_mask("SigAction::action", SIGACT);
	m_sa = *sa_;
}

inline SIGACTION *
SigAction::
action ()
{
	trace_with_mask("SigAction::action", SIGACT);

	return &m_sa;
}

inline void
SigAction::
flags (int new_flags_)
{
	trace_with_mask("void SigAction::flags()", SIGACT);

	m_sa.sa_flags = new_flags_;
}

inline int
SigAction::
flags ()
{
	trace_with_mask("int SigAction::flags()", SIGACT);

	return m_sa.sa_flags;
}

inline void
SigAction::
mask (SigSet & mask_set_)
{
	trace_with_mask("void SigAction::mask()", SIGACT);
  
	m_sa.sa_mask = *mask_set_;
}

inline SigSet
SigAction::
mask ()
{
	trace_with_mask("SigSet SigAction::mask()", SIGACT);

	SigSet tmpset(&m_sa.sa_mask);
	return tmpset;
}

inline void
SigAction::
handler (C_SIG_HANDLER sha_)
{
	trace_with_mask("void SigAction::handler()", SIGACT);

	*(C_SIG_HANDLER*) &m_sa.sa_handler = (C_SIG_HANDLER) sha_;
}

inline C_SIG_HANDLER
SigAction::
handler ()
{
	trace_with_mask("C_SIG_HANDLER SigAction::handler()", SIGACT);

	return (C_SIG_HANDLER) m_sa.sa_handler;
}

inline 
SigAction::operator SIGACTION * ()
{
	trace_with_mask("SigAction::operator SIGACTION * ()", SIGACT);

	return &m_sa;
}

inline int 
SigAction::
register_action (int signum_, SigAction* oaction_)
{
	trace_with_mask("SigAction::register_action()", SIGACT);

	/*--- place here recursive mutex lock to guard ... ---*/
	struct sigaction *osa = oaction_ == 0 ? 0 : oaction_->action();
	return sigaction(signum_, &m_sa, osa);
}

inline int
SigAction::
restore_action (int signum_, SigAction& oaction_)
{
	trace_with_mask("SigAction::restore_action()", SIGACT);

	m_sa = *oaction_.action();
	return sigaction(signum_, &m_sa, 0);
}

inline int 
SigAction::
retrieve_action (int signum_)
{
	trace_with_mask("SigAction::retrieve_action()", SIGACT);

	return sigaction(signum_, 0, &m_sa);
}

} // end namespace ASSA


#endif /* _SigAction_h */
