/******************************************************************************/
/*                                                                            */
/* "@(#)45        1.8  src/examples/ems/consumer.c, examples.src, os2dce21.dss, 960602a.1  4/6/96  10:31:27"                                         */
/*                                                                            */
/* SAMPLE CONSUMER: consumer.c                                                */
/*                                                                            */
/* COPYRIGHT:                                                                 */
/* ----------                                                                 */
/* Copyright (C) International Business Machines Corp., 1995.                 */
/*                                                                            */
/* DISCLAIMER OF WARRANTIES:                                                  */
/* -------------------------                                                  */
/* The following [enclosed] code is sample code created by IBM                */
/* Corporation.  This sample code is not part of any standard IBM product     */
/* and is provided to you solely for the purpose of assisting you in the      */
/* development of your applications.  The code is provided "AS IS",           */
/* without warranty of any kind.  IBM shall not be liable for any damages     */
/* arising out of your use of the sample code, even if they have been         */
/* advised of the possibility of such damages.                                */
/*                                                                            */
/******************************************************************************/
/*                                                                            */
/*  Sample to demonstrate an Event Management Service consumer.               */
/*                                                                            */
/******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#ifdef IBMOS2
#include <process.h>
#else
#include <unistd.h>
#define GETPID getpid
#endif
#include <signal.h>
#include <pthread.h>
#include <time.h>

#include <dce/dce.h>
#include <dce/dce_error.h>
#include <dce/dce_cf.h>
#include <dce/dce_svc.h>
#include <dce/ems.h>
#include <dce/keymgmt.h>
#include <dce/sec_login.h>
#include <dce/secsts.h>
#include "consumer.h"


#define DEFAULT_CONS_NAME           "sampleConsumer"
#define DEFAULT_SIZE                 sizeof(DEFAULT_CONS_NAME)+16
#define PRINCIPAL_NAME              "consumer"

/* filter group names... */
static ems_string_t  compSup = (ems_string_t)"compSup";
static ems_string_t  compSupAndLessThanOrEqualError
                             = (ems_string_t)"compSupAndLessThanOrEqualError";

ems_handle_t         emsHandle1, emsHandle2;

/* severities */
static char *sev[] =
               {"INVALID","FATAL","ERROR","WARNING","NOTICE","NOTICE_VERBOSE"};

extern void MaintainLoginContext        (pthread_addr_t);
extern void ManageKey                   (pthread_addr_t);
extern int  EstablishLoginContextAndKey (void);

boolean     does_filter_exist           (ems_handle_t*,ems_string_t);
void        BuildEventText              (ems_event_t*, char*, int);
void        ctrl_break_handler          (void);

/*----------------------------------------------------------------------------*/
/* This is the event handler defined on the ems_consumer_handler_register API */
/*   call in the mainline code...                                             */
/*                                                                            */
/* This is where you might want to do creative things based on the type of    */
/*   event that has been received.                                            */
/*----------------------------------------------------------------------------*/
void log_event(ems_event_t *event,error_status_t *status)
{
   int   i;
   char  text[1025];

/* Build the event text for display (ie, SVC event schema "format" attr... */
   BuildEventText(event,&text[0],1024);

/* determine the severity of the event */
   i = event->item[14].value.tagged_union.ushort_int;
   if ( i >= sizeof(sev)/sizeof(sev[0])) i = 0;

/* event processing... display SVC event schema "format" attribute... */
   fprintf(stdout,"%s: %s\n",sev[i],text);

/* return good status */
   *status = error_status_ok;
}

/*----------------------------------------------------------------------------*/
/* This is the mainline code for the sample consumer... it must perform the   */
/* following functions:                                                       */
/*                                                                            */
/*    1) call the ems_consumer_start API to start the event consumer          */
/*    2) call the ems_consumer_handler_register API to register an event      */
/*       handler (ie, log_event in this case)                                 */
/*    3) call the ems_consumer_register API to register with the EMSD         */
/*       (ie, the EMS daemon) that this consumer is interested in receiving   */
/*       events from...                                                       */
/*    4) setup the event filter expressions and/or event filter groups        */
/*    5) call the rpc_server_listen API to start listening for events         */
/*----------------------------------------------------------------------------*/
main()
{
   ems_filter_exp_list_t *expList1, *expList2;
   ems_netname_t          netname;
   error_status_t         status;
   ems_filtername_list_t *fnList;
   char                  *server_name;


   pthread_inst_exception_handler();
#if defined (__BORLANDC__)
   signal (SIGINT,   (void (_USERENTRY *)(int))ctrl_break_handler);
   signal (SIGTERM,  (void (_USERENTRY *)(int))ctrl_break_handler);
   signal (SIGABRT,  (void (_USERENTRY *)(int))ctrl_break_handler);
   signal (SIGBREAK, (void (_USERENTRY *)(int))ctrl_break_handler);
#elif defined (__WATCOMC__)
   signal (SIGINT,   (void (*)(int))ctrl_break_handler);
   signal (SIGTERM,  (void (*)(int))ctrl_break_handler);
   signal (SIGABRT,  (void (*)(int))ctrl_break_handler);
   signal (SIGBREAK, (void (*)(int))ctrl_break_handler);
#else
   signal(SIGINT,  (_SigFunc) ctrl_break_handler);
   signal(SIGTERM, (_SigFunc) ctrl_break_handler);
   signal(SIGABRT, (_SigFunc) ctrl_break_handler);
   signal(SIGBREAK,(_SigFunc) ctrl_break_handler);
#endif

/* Generate the name of the consumer */
   server_name=(char*)malloc(DEFAULT_SIZE);
   if (server_name==NULL)
   {
      printf("*** Insufficient memory ***\n");
      exit(1);
   }

   sprintf( server_name, "%s%d", DEFAULT_CONS_NAME, getpid());
   printf("    Name of the sample consumer = %s\n",server_name);

/* Establish network credentials and login context.  This was added to */
/*   the sample code to keep permissions valid should you leave this   */
/*   running for long periods of time                                  */
   if (!EstablishLoginContextAndKey())
   {
      status=1;
      CHECK_STATUS("*** ALERT: EstablishLoginContextAndKey failed!\n",&status);
   }

/* This is called to start the event consumer */
   printf("    Start the sample consumer\n");
   ems_consumer_start
      (server_name,           /* name given to this instance of the consumer */
       0,                     /* start flags - should be zeros               */
       &status);              /* address of status - set in start code       */
   CHECK_STATUS("ems_consumer_start", &status);  /* checks returned status   */

   free(server_name);

/* declare the event handler */
   printf("    Enable the sample consumer event handler\n");
   ems_consumer_handler_register
      (log_event,        /* name of the function that will process the event */
       &status);         /* address of status - set in registration code     */
   CHECK_STATUS("ems_consumer_handler_register", &status);

/* allocate memory for the first event filter expression list */
   expList1=(ems_filter_exp_list_t *)malloc(
                    sizeof(ems_filter_exp_list_t)+1*sizeof( ems_filter_exp_t));
   if (expList1==NULL) {
      printf("*** Insufficient memory ***\n");
      exit(1);
   }

/* define a filter... allows all events from SVC event component, "sup". */
   expList1->filter_exps[0].attr_name=(unsigned char *)"component_name";
   expList1->filter_exps[0].attr_operator=ems_c_attr_op_eq;
   expList1->filter_exps[0].attr_value.format=ems_c_attr_char_string;
   expList1->filter_exps[0].attr_value.tagged_union.char_string =
                                                       (unsigned char *)"sup";
   expList1->size=1;   /* indicates the number of filter expressions in list */

/* allocate memory for another event filter expression list */
   expList2=(ems_filter_exp_list_t *)malloc(
                    sizeof(ems_filter_exp_list_t)+1*sizeof( ems_filter_exp_t));
   if (expList2==NULL) {
      printf("*** Insufficient memory ***\n");
      exit(1);
   }

/* define another filter... allows all events from SVC event component, */
/*   "sup" that are either FATAL or ERROR events                        */
   expList2->filter_exps[0].attr_name=(unsigned char *)"component_name";
   expList2->filter_exps[0].attr_operator=ems_c_attr_op_eq;
   expList2->filter_exps[0].attr_value.format = ems_c_attr_char_string;
   expList2->filter_exps[0].attr_value.tagged_union.char_string =
                                                        (unsigned char *)"sup";
   expList2->filter_exps[1].attr_name=(unsigned char *)"attribute.severity";
   expList2->filter_exps[1].attr_operator=ems_c_attr_op_le;
   expList2->filter_exps[1].attr_value.format=ems_c_attr_ushort_int;
   expList2->filter_exps[1].attr_value.tagged_union.ushort_int=SVC_C_SEV_ERROR;
   expList2->size = 2; /* indicates the number of filter expressions in list */

/* allocate memory for the event filter name list */
   fnList=(ems_filtername_list_t *)malloc(
                       sizeof(ems_filtername_list_t)+1*sizeof( ems_string_t ));
   if (fnList==NULL) {
      printf("+*** Insufficient memory ***\n");
      exit(1);
   }

   TRY
   {
      netname.service=ems_ns_dce; /* DCE CDS name service... indicates which */
                                  /* name service will recognize the network */
                                  /* name being defined here...              */
   /* allocate memory for the network address... see HOST1 definition above..*/
      netname.netaddr=(ems_netaddr_t *)malloc(
                                        sizeof(ems_netaddr_t)+strlen(HOST1)+1);
      if (netname.netaddr==NULL) {
         printf("*** Insufficient memory ***\n");
         exit(1);
      }

   /* complete the definition of the network name structure...len, name...*/
      netname.netaddr->len=strlen(HOST1)+1;
      strcpy((char *)&netname.netaddr->name[0],HOST1);

   /* register the network name with EMS (Event Management Service) */
      printf("    Register with %s\n",&netname.netaddr->name[0]);
      ems_consumer_register
         (&netname,            /* address of network name just defined       */
          NULL,                /* requires an ems_add_filter_to_group later  */
          &emsHandle1,         /* address of handle set by registration code */
          &status);            /* address of status set by registration code */
      CHECK_STATUS("ems_consumer_register", &status);

      if (does_filter_exist(&emsHandle1,compSup)==FALSE)
      {
         ems_filter_add
            (emsHandle1,      /* handle returned by registration             */
             compSup,         /* name of filter                              */
             ems_c_svc_type,  /* filter event type                           */
             expList1,        /* list of filter expressions                  */
             &status);        /* address of status set by registration code  */
         CHECK_STATUS("ems_filter_add",&status);
      }

      if (does_filter_exist(&emsHandle1,compSupAndLessThanOrEqualError)==FALSE)
      {
         ems_filter_add
            (emsHandle1,          /* handle returned by registration         */
             compSupAndLessThanOrEqualError, /* name of filter               */
             ems_c_svc_type,      /* filter event type                       */
             expList2,            /* list of filter expressions              */
             &status);            /* addr of status set by registration code */
         CHECK_STATUS("ems_filter_add", &status);
      }

   /* 2 filters were defined but only the first is used with HOST1 */
      fnList->size=1;        /* indicates the number of filter names in list */
      fnList->filter_names[0] = compSup;

      printf("    Add filter compSup to filter group\n");
      ems_add_filter_to_group
         (emsHandle1,        /* handle returned by registration */
          fnList,            /* list of filter names */
          &status);          /* address of status set by registration code */
      CHECK_STATUS("ems_add_filter_to_group", &status);

      free(netname.netaddr);

#ifdef _TWOHOSTS
   /* allocate memory for a second network address... */
      netname.netaddr=(ems_netaddr_t *)malloc(
                                       sizeof(ems_netaddr_t)+strlen(HOST2)+1);
      if (netname.netaddr==NULL) {
         printf("*** Insufficient memory ***\n");
         exit(1);
      }

   /* complete the definition of the network name structure...len, name... */
      netname.netaddr->len=strlen(HOST2)+1;
      strcpy( (char *)&netname.netaddr->name[0],HOST2);

   /* register the network name with EMS (Event Management Service) */
      printf("    Register with %s\n",&netname.netaddr->name[0]);
      ems_consumer_register
         (&netname,            /* address of network name just defined       */
          NULL,                /* requires an ems_add_filter_to_group later  */
          &emsHandle2,         /* address of handle set by registration code */
          &status);            /* address of status set by registration code */
      CHECK_STATUS("ems_consumer_register", &status);

      if (does_filter_exist(&emsHandle2,compSup)==FALSE)
      {
         ems_filter_add
            (emsHandle2,       /* handle returned by registration            */
             compSup,          /* name of filter                             */
             ems_c_svc_type,   /* filter event type                          */
             expList1,         /* list of filter expressions                 */
             &status);         /* address of status set by registration code */
         CHECK_STATUS("ems_filter_add",&status);
      }

      if (does_filter_exist(&emsHandle2,compSupAndLessThanOrEqualError)==FALSE)
      {
         ems_filter_add
            (emsHandle2,       /* handle returned by registration            */
             compSupAndLessThanOrEqualError,  /* name of filter              */
             ems_c_svc_type,   /* filter event type                          */
             expList2,         /* list of filter expressions                 */
             &status);         /* addr of status set by registration code    */
         CHECK_STATUS("ems_filter_add", &status);
      }
   /* note that 2 filter groups were defined but only the former is used */
      fnList->size=1;        /* indicates the number of filter names in list */
      fnList->filter_names[0] = compSupAndLessThanOrEqualError;

      printf("    Add filter compSupAndLessThanOrEqualError to filter group\n");
      ems_add_filter_to_group
         (emsHandle2,          /* handle returned by registration */
          fnList,              /* list of filter names */
          &status);            /* address of status set by registration code */
      CHECK_STATUS("ems_add_filter_to_group", &status);

      free(netname.netaddr);
#endif

   /* start listening for events */
      printf("    Start listening for events\n");
      rpc_server_listen( 5, &status );
   }

   CATCH_ALL
   {
      printf("*** EXCEPTION OCCURRED:  Consumer is terminating ***");
   }

   ENDTRY

/* cleanup and exit... */
   printf("    Start cleanup\n");
   ems_consumer_unregister( &emsHandle1, &status );
#ifdef _TWOHOSTS
   ems_consumer_unregister( &emsHandle2, &status );
#endif
   ems_consumer_stop( &status );

   signal(SIGSEGV, SIG_DFL);
   signal(SIGABRT, SIG_DFL);

   pthread_dinst_exception_handler();
   return(0);

} /* end main */

/*****************************************************************************/
/* Name                         = EstablishLoginContextAndKey()              */
/* Function                     = Gets network credentials and establishes   */
/*                                the login context.                         */
/* Input                        = <none>                                     */
/* Output                       = <none>                                     */
/* Return                       = TRUE/FALSE                                 */
/*****************************************************************************/
int EstablishLoginContextAndKey(void)
{
   sec_login_handle_t           login_context;
   sec_login_auth_src_t         auth_src;
   void                        *server_key;
   error_status_t               status;
   boolean32                    identity_valid;
   boolean32                    reset_passwd;
   int                          rstatus = TRUE;
   pthread_t                    refresh_login_context_thread;
   pthread_t                    manage_key_thread;


   do {
   /*------------------------------------------------------------------------
   /  The sec_login_setup_identity routine creates any local context
   /  necessary to perform authenticated network operations.  It does
   /  not establish any local operating system context; that is the
   /  responsibility of the caller.  It is the standard network login
   /  routine.  The network identity set up by this operation cannot be
   /  used until it is validated by means of sec_login_validate_identity.
   /  However, the sec_key_mgmt_get_key routine must be called first to
   /  extract the specified key from the local key storage.
   /
   /  The sec_login_setup_identity and the sec_login_validate_identity
   /  operations are two halves of a single logical operation.  Together
   /  they collect the identity data needed to establish an authenticated
   /  identity.
   /-------------------------------------------------------------------------*/
      sec_login_setup_identity((unsigned_char_p_t)PRINCIPAL_NAME,
                               sec_login_no_flags,
                               &login_context,&status);
      CHECK_STATUS("*** ALERT: sec_login_setup_identity failed!",&status);

      sec_key_mgmt_get_key(rpc_c_authn_dce_secret,NULL,
                           (idl_char *)PRINCIPAL_NAME,
                           0,&server_key,&status);
      CHECK_STATUS("*** ALERT: sec_key_mgmt_get_key failed!",&status);

      identity_valid = sec_login_validate_identity(login_context,
                                                  (sec_passwd_rec_t*)server_key,
                                                   &reset_passwd,&auth_src,
                                                   &status);
      CHECK_STATUS("*** ALERT: sec_login_validate_identity failed!",&status);

   /*------------------------------------------------------------------------
   /  The sec_key_mgmt_free_key routine releases any storage allocated
   /  for the indicated key data by sec_key_mgmt_get_key. The storage
   /  for the key data returned by sec_key_mgmt_get_key was dynamically
   /  allocated.
   /-------------------------------------------------------------------------*/
      sec_key_mgmt_free_key(&server_key,&status);
      CHECK_STATUS("*** ALERT: sec_key_mgmt_free_key failed!",&status);

      if (!(identity_valid && auth_src == sec_login_auth_src_network)) {
         rstatus = FALSE;
         break;
      }

   /*------------------------------------------------------------------------
   /  The sec_login_set_context routine sets the network credentials to those
   /  specified by the previously validated login context.
   /
   /  You cannot perform this function if the context is marked as private (ie,
   /  sec_login_credentials_private).  Such a context can only be used as an
   /  explicit context in user space applications.
   /-------------------------------------------------------------------------*/
      sec_login_set_context(login_context,&status);
      CHECK_STATUS("*** ALERT: sec_login_set_context failed!",&status);

   /*------------------------------------------------------------------------
   /  Start a thread to maintain the login context
   /-------------------------------------------------------------------------*/
      TRY {
         pthread_create(&refresh_login_context_thread,
                        pthread_attr_default,
                       (pthread_startroutine_t)MaintainLoginContext,
                       (pthread_addr_t)login_context);
         pthread_detach(&refresh_login_context_thread);
      }
      CATCH_ALL {
         status=1;
         CHECK_STATUS("*** ALERT: THREAD CREATE ERROR!",&status);
      }
      ENDTRY


   /*------------------------------------------------------------------------
   /  Start a thread to manage the key
   /-------------------------------------------------------------------------*/
      TRY {
         pthread_create(&manage_key_thread,
                        pthread_attr_default,
                       (pthread_startroutine_t)ManageKey,
                       (pthread_addr_t)NULL);
         pthread_detach(&manage_key_thread);
      }
      CATCH_ALL {
         status=1;
         CHECK_STATUS("*** ALERT: THREAD CREATE ERROR!",&status);
      }
      ENDTRY

   } while(0);

   return rstatus;

} /* end EstablishLoginContextAndKey */


/*****************************************************************************/
/* Name                         = MaintainLoginContext()                     */
/* Function                     = Maintains login context.  This code        */
/*                                executes in the thread created in the      */
/*                                EstablishLoginContextAndKey() routine.     */
/* Input                        = parg - DCE pthread args                    */
/* Output                       = <none>                                     */
/* Return                       = <none>                                     */
/*****************************************************************************/
extern void MaintainLoginContext(pthread_addr_t parg)
{
   sec_login_handle_t           login_context = (sec_login_handle_t)parg;
   signed32                     expiration;
   signed32                     delay_time;
   struct timespec              delay;
   boolean32                    reset_passwd;
   boolean32                    identity_valid;
   boolean32                    *server_key;
   sec_login_auth_src_t         auth_src;
   error_status_t               status;
   time_t                       cur_time;

   while (1) {
      time(&cur_time);

      printf("\n*** Maintain Login Context ENTRY ***\n");
      printf("      %s\n",asctime(localtime(&cur_time)));

   /*------------------------------------------------------------------------
   /  The sec_login_get_expiration routine extracts the lifetime for the
   /  TGT that belongs to the authenticated identity contained in the login
   /  context.  The lifetime value is filled in if available, otherwise it
   /  is set to 0 (zero).  This routine allows an application to tell an
   /  interactive user how long the user's network login (and authenticated
   /  identity) will last before it must be refreshed.
   /
   /  This routine works only on previously validated contexts.
   /-------------------------------------------------------------------------*/
      sec_login_get_expiration(login_context,&expiration,&status);
      if ((status != rpc_s_ok) && (status != sec_login_s_not_certified)) {
         CHECK_STATUS("*** ALERT: sec_login_get_expiration failed!",&status);
      }

   /*------------------------------------------------------------------------
   /  The pthread_delay_np routine causes a thread to delay execution for a
   /  specified period of elapsed wall clock time.  The period of time the
   /  thread waits is at least as long as the number of seconds and nanoseconds
   /  specified in the interval parameter.
   /-------------------------------------------------------------------------*/
      if ((delay_time = expiration-cur_time-(600)) > 0) { /* 600 sec = 10 min*/
         delay.tv_sec = delay_time;     /* An integer number of seconds.     */
         delay.tv_nsec = 0;             /* An integer number of nanoseconds. */
         pthread_delay_np(&delay);
      }

   /*------------------------------------------------------------------------
   /  The sec_login_refresh_identity routine refreshes a previously established
   /  identity.  It operates on an existing valid context, and cannot be used
   /  to change credentials associated with that identity.  The refreshed
   /  identity reflects changes that affect ticket lifetimes, but not other
   /  changes.  For example, the identity reflects a change to maximum ticket
   /  lifetime, but does not reflect the addition of the identity as a member
   /  to a group.  Only a DCE login reflects all administrative changes that
   /  have been made since the last login.
   /
   /  The network identity set up by this operation cannot be used until
   /  it is validated by means of sec_login_validate_identity.  However,
   /  the sec_key_mgmt_get_key routine must be called first to extract the
   /  specified key from the local key storage.
   /
   /  The sec_login_refresh_identity and the sec_login_validate_identity
   /  operations are two halves of a single logical operation.  Together
   /  they collect the identity data needed to establish an authenticated
   /  identity.
   /
   /  It is an error to refresh a locally authenticated context.
   /-------------------------------------------------------------------------*/
      sec_login_refresh_identity(login_context,&status);
      CHECK_STATUS("*** ALERT: sec_login_refresh_identity failed!",&status);

      sec_key_mgmt_get_key(rpc_c_authn_dce_secret,NULL,
                           (idl_char *)PRINCIPAL_NAME,
                           0,(idl_void_p_t *)&server_key,&status);
      CHECK_STATUS("*** ALERT: sec_key_mgmt_get_key failed",&status);

      identity_valid = sec_login_validate_identity(login_context,
                                                  (sec_passwd_rec_t*)server_key,
                                                   &reset_passwd,&auth_src,
                                                   &status);
      CHECK_STATUS("*** ALERT: sec_login_validate_identity failed!",&status);

   /*------------------------------------------------------------------------
   /  The sec_key_mgmt_free_key routine releases any storage allocated
   /  for the indicated key data by sec_key_mgmt_get_key. The storage
   /  for the key data returned by sec_key_mgmt_get_key was dynamically
   /  allocated.
   /-------------------------------------------------------------------------*/
      sec_key_mgmt_free_key(&server_key,&status);
      CHECK_STATUS("*** ALERT: sec_key_mgmt_free_key failed!",&status);

      if (!identity_valid) {
         status=1;
         CHECK_STATUS("*** ALERT: INVALID IDENTITY!",&status);
         pthread_exit((pthread_addr_t)1);
      }

   } /* end while(1) */

} /* end MaintainLoginContext */

/*****************************************************************************/
/* Name                         = ManageKey()                                */
/* Function                     = Manages the key.  This code runs in thread */
/*                                created in EstablishLoginContextAndKey()   */
/*                                routine.                                   */
/* Input                        = parg - NULL for this routine...            */
/* Output                       = <none>                                     */
/* Return                       = <none>                                     */
/*****************************************************************************/
extern void ManageKey(pthread_addr_t parg)
{
   error_status_t status;

   while (1) {
   /*------------------------------------------------------------------------
   /  The sec_key_mgmt_manage_key routine changes the specified principal's
   /  key on a regular basis, as determined by the local cell's policy.  It
   /  runs indefinitely and never returns during normal operation.  It should
   /  be called only from a thread that has been devoted to managing keys.
   /
   /  This routine queries the DCE Registry to determine the password
   /  expiration policy that applies to the named principal.  It then idles
   /  until a short time before the current key is due to expire and uses the
   /  sec_key_mgmt_gen_rand_key to produce a new random key.  It updates both
   /  the local key storage and the DCE Registry.
   /-------------------------------------------------------------------------*/
      sec_key_mgmt_manage_key(rpc_c_authn_dce_secret,
                              NULL,
                              (idl_char *)PRINCIPAL_NAME,
                              &status);
      CHECK_STATUS("*** ALERT: sec_key_mgmt_manage_key failed!",&status);
   }

} /* end ManageKey */

/*****************************************************************************/
/* Name                         = does_filter_exist()                        */
/* Function                     = Check to see if a filter already exists.   */
/*                              =============================================*/
/*                              = The ems_filter_get API could have been     */
/*                              = used instead of implementing this routine. */
/*                              =============================================*/
/* Input                        = EMSD handle and filter name                */
/* Output                       = <none>                                     */
/* Return                       = TRUE if it exists, FALSE is it does not.   */
/*****************************************************************************/
boolean does_filter_exist( ems_handle_t *handle, ems_string_t filter_name )
{
   int                    i;
   error_status_t         status;
   ems_filtername_list_t *fnList;

   ems_filter_get_namelist(*handle,&fnList,&status);
   if (status) return FALSE;

   for (i=0;i<fnList->size;i++)
   {
      if (strcmp((char*)fnList->filter_names[i],(char *)filter_name)==0)
      {
         ems_filter_free_namelist(&fnList,&status);
         return TRUE;
         break;
      }
   }

   ems_filter_free_namelist(&fnList,&status);
   return FALSE;

} /* end does_filter_exist */

/*****************************************************************************/
/* Name       = BuildEventText()                                             */
/* Function   = Builds event message text using the SVC format attribute     */
/*              which contains the %s, %d, etc.  It is also supposed to      */
/*              handle the cases where %1$d, %2$s, %3$6ld, %6ld, etc.        */
/* Input      = event  - EMS event                                           */
/*            = pTxt   - Pointer to where the resolved text is placed        */
/*            = maxlen - Max length of pTxt area                             */
/* Output     = pTxt   - Updated...                                          */
/* Return     = <none>                                                       */
/*****************************************************************************/
#define SVC_FORMAT_ATTR    6           /* INDEX into SVC event type schema */
#define SVC_ARGTYPES_ATTR  2

void BuildEventText(ems_event_t *event,char *pText, int maxlen)
{
   int          cntr=16;                     /* first 16 items are for SVC */
   char        *pTmp=NULL;
   char        *pTxt=pText;
   size_t       len;
   char const  *pBeg=(const char*)event->
                      item[SVC_FORMAT_ATTR].value.tagged_union.char_string;
   char const  *pEnd;
   char        *pArg=NULL;
   int          islong=FALSE;
   char         fmt[32];
   char         realfmt[32];
   size_t       realcnt=0;
   char         cnt[2];
   size_t       fmtlen=0;
   int          done;

   if ((pText==NULL)||(maxlen==0)) return;

/* If no args to be parsed, just copy the message. */
   if (event->count<=16)
   {
      strcpy(pTxt,pBeg);
      return;
   }

   pArg=strstr(pBeg,"%");

/* If no args to be parsed, just copy the message. */
   if (!pArg)
   {
      strcpy(pTxt,pBeg);
      return;
   }

   pTmp=(char*)malloc(maxlen+1);

/* If cannot get memory, just copy the message. */
   if (!pTmp)
   {
      strcpy(pTxt,pBeg);
      return;
   }

   pTmp[0]=0x00;

/* Parse the message and fill in the args. */
   for (done=FALSE; !done; pArg++)
   {
      fmt[fmtlen++]=*pArg;

      switch (*pArg)
      {
         default:
            if ((*pArg>'0')&&(*pArg<='9'))
            {
               if (*(pArg+1)=='$')
               {
                  cnt[0]=*pArg;
                  cnt[1]=0;
                  cntr=atoi(cnt);
                  cntr+=15;
               }
            }
            continue;
         case '%':
            memset(fmt,0x00,sizeof(fmt));
            fmtlen=1;
            fmt[0]='%';
            memset(realfmt,0x00,sizeof(realfmt));
            realcnt=1;
            realfmt[0]='%';
            continue;
         case 'l':
            islong=TRUE;
            realfmt[realcnt++]='l';
            continue;
         case 'd': case 'i': case 'o': case 'x': case 'X':
            if (!(pEnd=strstr(pBeg,fmt)))
            {
               strcpy(pTxt,pBeg);
               done=TRUE;
               break;
            }

            realfmt[realcnt++]=*pArg;
            len=pEnd-pBeg;
            memcpy(pTmp,pBeg,len);
            *(pTmp+len)=0;
            pBeg=pEnd+strlen(fmt);
            strcat(pTmp,realfmt);

            if (islong)
               sprintf(pTxt,pTmp,
                       event->item[cntr++].value.tagged_union.long_int);
            else
               sprintf(pTxt,pTmp,
                       event->item[cntr++].value.tagged_union.short_int);

            pTxt+=strlen(pTxt);
            done=TRUE;
            break;
         case 'u':
            if (!(pEnd=strstr(pBeg,fmt))) {
               strcpy(pTxt,pBeg);
               done=TRUE;
               break;
            }

            realfmt[realcnt++]=*pArg;
            len=pEnd-pBeg;
            memcpy(pTmp,pBeg,len);
            *(pTmp+len)=0;
            pBeg=pEnd+strlen(fmt);
            strcat(pTmp,realfmt);

            if (islong)
               sprintf(pTxt,pTmp,
                       event->item[cntr++].value.tagged_union.ulong_int);
            else
               sprintf(pTxt,pTmp,
                       event->item[cntr++].value.tagged_union.ushort_int);

            pTxt += strlen(pTxt);
            done=TRUE;
            break;
         case 'f': case 'e': case 'E': case 'g': case 'G':
            if (!(pEnd=strstr(pBeg,fmt))) {
               strcpy(pTxt,pBeg);
               done=TRUE;
               break;
            }

            realfmt[realcnt++]=*pArg;
            len=pEnd-pBeg;
            memcpy(pTmp,pBeg,len);
            *(pTmp+len)=0;
            pBeg=pEnd+strlen(fmt);
            strcat(pTmp,realfmt);

            sprintf(pTxt,pTmp,
                    event->item[cntr++].value.tagged_union.long_float);
            pTxt+=strlen(pTxt);
            done=TRUE;
            break;
         case 'c':
            if (!(pEnd=strstr(pBeg,fmt))) {
               strcpy(pTxt,pBeg);
               done=TRUE;
               break;
            }

            realfmt[realcnt++]=*pArg;
            len=pEnd-pBeg;
            memcpy(pTmp,pBeg,len);
            *(pTmp+len)=0;
            pBeg=pEnd+strlen(fmt);
            strcat(pTmp,realfmt);

            sprintf(pTxt,pTmp,
                    event->item[cntr++].value.tagged_union.ushort_int);
            pTxt+=strlen(pTxt);
            done=TRUE;
            break;
         case 's':
            if (!(pEnd=strstr(pBeg,fmt))) {
               strcpy(pTxt,pBeg);
               done=TRUE;
               break;
            }

            realfmt[realcnt++]=*pArg;
            len=pEnd-pBeg;
            memcpy(pTmp,pBeg,len);
            *(pTmp+len)=0;
            pBeg=pEnd+strlen(fmt);
            strcat(pTmp,realfmt);

            sprintf(pTxt,pTmp,
                    event->item[cntr++].value.tagged_union.char_string);
            pTxt+=strlen(pTxt);
            done=TRUE;
            break;
         case 'p':
            if (!(pEnd=strstr(pBeg,fmt))) {
               strcpy(pTxt,pBeg);
               done=TRUE;
               break;
            }

            realfmt[realcnt++]=*pArg;
            len=pEnd-pBeg;
            memcpy(pTmp,pBeg,len);
            *(pTmp+len)=0;
            pBeg=pEnd+strlen(fmt);
            strcat(pTmp,realfmt);

            sprintf(pTxt,pTmp,
                    event->item[cntr++].value.tagged_union.byte_string);
            pTxt+=strlen(pTxt);
            done=TRUE;
            break;
         case 'b':
            if (!(pEnd=strstr(pBeg,fmt))) {
               strcpy(pTxt,pBeg);
               done=TRUE;
               break;
            }

            realfmt[realcnt++]=*pArg;
            len=pEnd-pBeg;
            memcpy(pTmp,pBeg,len);
            *(pTmp+len)=0;
            pBeg=pEnd+strlen(fmt);
            strcat(pTmp,realfmt);

            sprintf(pTxt,pTmp,
                    event->item[cntr++].value.tagged_union.bytes.data);
            pTxt+=strlen(pTxt);
            done=TRUE;
            break;
      }

      islong=FALSE;

      if (done==TRUE)
      {
         pArg=strstr(pBeg,"%");
         if (pArg)
         {
            pArg--;
            done=FALSE;
         }
      }
   } /* end of for (done=FALSE; !done; pArg++) */

/* Copy the remaining format string. */
   if (*pBeg) strcpy(pTxt,pBeg);

   free(pTmp);

} /* end BuildEventText */


void ctrl_break_handler(void)
{
   error_status_t         status;

   signal(SIGINT,  SIG_IGN);
   signal(SIGTERM, SIG_IGN);
   signal(SIGABRT, SIG_IGN);
   signal(SIGBREAK,SIG_IGN);

   printf("    Start cleanup in signal handler\n");
   ems_consumer_unregister( &emsHandle1, &status );
#ifdef _TWOHOSTS
   ems_consumer_unregister( &emsHandle2, &status );
#endif
   ems_consumer_stop( &status );

   exit(EXIT_SUCCESS);

} /* end ctrl_break_handler */
