/*
 *  Swrun MIB architecture support
 *
 * $Id: swrun.c 15768 2007-01-22 16:18:29Z rstory $
 */
/*
 * Copyright (C) 2007 Apple, Inc. All rights reserved.
 * Use is subject to license terms specified in the COPYING file
 * distributed with the Net-SNMP package.
 */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-features.h>
#include <net-snmp/net-snmp-includes.h>

#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/data_access/swrun.h>
#include "swrun.h"
#include "swrun_private.h"

#ifdef HAVE_PCRE_H
#include <pcre.h>
#endif

netsnmp_feature_child_of(software_running, libnetsnmpmibs);

netsnmp_feature_child_of(swrun_max_processes, software_running);
netsnmp_feature_child_of(swrun_count_processes_by_name, software_running);
netsnmp_feature_child_of(swrun_count_processes_by_regex, software_running);

/**---------------------------------------------------------------------*/
/*
 * local static vars
 */
static int _swrun_init = 0;
       int _swrun_max  = 0;
static netsnmp_cache     *swrun_cache     = NULL;
static netsnmp_container *swrun_container = NULL;

/*
 * local static prototypes
 */
static void _swrun_entry_release(void* entry, void *unused);

/**
 * initialization
 */
void
init_swrun(void)
{
    DEBUGMSGTL(("swrun:access", "init\n"));

    if (1 == _swrun_init)
        return;

    _swrun_init = 1;

    (void)netsnmp_swrun_container();
    netsnmp_arch_swrun_init();
    (void) netsnmp_swrun_cache();
}

void
shutdown_swrun(void)
{
    DEBUGMSGTL(("swrun:access", "shutdown\n"));

}

int
swrun_count_processes(int include_kthreads)
{
    netsnmp_swrun_entry *entry;
    netsnmp_iterator  *it;
    int i = 0;

    netsnmp_cache_check_and_reload(swrun_cache);
    if ( !swrun_container )
        return 0;    /* or -1 */

    if (include_kthreads)
        return ( swrun_container ? CONTAINER_SIZE(swrun_container) : 0 );

    it = CONTAINER_ITERATOR( swrun_container );
    while ((entry = (netsnmp_swrun_entry*)ITERATOR_NEXT( it )) != NULL) {
        if (4 == entry->hrSWRunType)
            i++;
    }
    ITERATOR_RELEASE( it );

    return i;
}

#ifndef NETSNMP_FEATURE_REMOVE_SWRUN_MAX_PROCESSES
int
swrun_max_processes( void )
{
    return _swrun_max;
}
#endif /* NETSNMP_FEATURE_REMOVE_SWRUN_MAX_PROCESSES */

#ifndef NETSNMP_FEATURE_REMOVE_SWRUN_COUNT_PROCESSES_BY_REGEX
#ifdef HAVE_PCRE_H
int
swrun_count_processes_by_regex( char *name, netsnmp_regex_ptr regexp )
{
    netsnmp_swrun_entry *entry;
    netsnmp_iterator  *it;
    int i = 0;
    int found_ndx[30];
    int found;
    char fullCommand[64 + 128 + 128 + 3];

    netsnmp_cache_check_and_reload(swrun_cache);
    if ( !swrun_container || !name || !regexp.regex_ptr )
        return 0;    /* or -1 */

    it = CONTAINER_ITERATOR( swrun_container );
    while ((entry = (netsnmp_swrun_entry*)ITERATOR_NEXT( it )) != NULL) {
        /* need to assemble full command back so regexps can get full picture */
        sprintf(fullCommand, "%s %s", entry->hrSWRunPath, entry->hrSWRunParameters);
        found = pcre_exec(regexp.regex_ptr, NULL, fullCommand, strlen(fullCommand), 0, 0, found_ndx, 30);
        if (found > 0) {
            i++;
        }
    }
    ITERATOR_RELEASE( it );

    return i;
}
#endif /* HAVE_PCRE_H */
#endif /* NETSNMP_FEATURE_REMOVE_SWRUN_COUNT_PROCESSES_BY_REGEX */

#ifndef NETSNMP_FEATURE_REMOVE_SWRUN_COUNT_PROCESSES_BY_NAME
int
swrun_count_processes_by_name( char *name )
{
    netsnmp_swrun_entry *entry;
    netsnmp_iterator  *it;
    int i = 0;

    netsnmp_cache_check_and_reload(swrun_cache);
    if ( !swrun_container || !name )
        return 0;    /* or -1 */

    it = CONTAINER_ITERATOR( swrun_container );
    while ((entry = (netsnmp_swrun_entry*)ITERATOR_NEXT( it )) != NULL) {
        if (0 == strcmp( entry->hrSWRunName, name ))
            i++;
    }
    ITERATOR_RELEASE( it );

    return i;
}
#endif /* NETSNMP_FEATURE_REMOVE_SWRUN_COUNT_PROCESSES_BY_NAME */

/**---------------------------------------------------------------------*/
/*
 * cache functions
 */

static int
_cache_load( netsnmp_cache *cache,  void *magic )
{
    netsnmp_swrun_container_load( swrun_container, 0 );
    return 0;
}

static void
_cache_free( netsnmp_cache *cache,  void *magic )
{
    netsnmp_swrun_container_free_items( swrun_container );
    return;
}

/**
 * create swrun cache
 */
netsnmp_cache *
netsnmp_swrun_cache(void)
{
    oid    hrSWRunTable_oid[]   = { 1, 3, 6, 1, 2, 1, 25, 4, 2 };
    size_t hrSWRunTable_oid_len = OID_LENGTH(hrSWRunTable_oid);

    if ( !swrun_cache ) {
        swrun_cache = netsnmp_cache_create(30,   /* timeout in seconds */
                           _cache_load,  _cache_free,
                           hrSWRunTable_oid, hrSWRunTable_oid_len);
        if (swrun_cache)
            swrun_cache->flags = NETSNMP_CACHE_DONT_INVALIDATE_ON_SET;
    }
    return swrun_cache;
}


/**---------------------------------------------------------------------*/
/*
 * container functions
 */
/**
 * create swrun container
 */
netsnmp_container *
netsnmp_swrun_container(void)
{
    DEBUGMSGTL(("swrun:container", "init\n"));

    /*
     * create the container.
     */
  if (!swrun_container) {
    swrun_container = netsnmp_container_find("swrun:table_container");
    if (NULL == swrun_container)
        return NULL;

    swrun_container->container_name = strdup("swrun container");
  }

    return swrun_container;
}

/**
 * load swrun information in specified container
 *
 * @param container empty container to be filled.
 *                  pass NULL to have the function create one.
 * @param load_flags flags to modify behaviour. Examples:
 *                   NETSNMP_SWRUN_ALL_OR_NONE
 *
 * @retval NULL  error
 * @retval !NULL pointer to container
 */
netsnmp_container*
netsnmp_swrun_container_load(netsnmp_container* user_container, u_int load_flags)
{
    netsnmp_container* container = user_container;
    int rc;

    DEBUGMSGTL(("swrun:container:load", "load\n"));
    netsnmp_assert(1 == _swrun_init);

    if (NULL == container)
        container = netsnmp_swrun_container();
    if (NULL == container) {
        snmp_log(LOG_ERR, "no container specified/found for swrun\n");
        return NULL;
    }

    rc =  netsnmp_arch_swrun_container_load(container, load_flags);
    if (0 != rc) {
        if (NULL == user_container) {
            netsnmp_swrun_container_free(container, NETSNMP_SWRUN_NOFLAGS);
            container = NULL;
        }
        else if (load_flags & NETSNMP_SWRUN_ALL_OR_NONE) {
            DEBUGMSGTL(("swrun:container:load",
                        " discarding partial results\n"));
            netsnmp_swrun_container_free_items(container);
        }
    }

    return container;
}

void
netsnmp_swrun_container_free(netsnmp_container *container, u_int free_flags)
{
    DEBUGMSGTL(("swrun:container", "free\n"));

    if (NULL == container) {
        snmp_log(LOG_ERR, "invalid container for netsnmp_swrun_container_free\n");
        return;
    }

    if(! (free_flags & NETSNMP_SWRUN_DONT_FREE_ITEMS))
        netsnmp_swrun_container_free_items(container);

    CONTAINER_FREE(container);
}

void
netsnmp_swrun_container_free_items(netsnmp_container *container)
{
    DEBUGMSGTL(("swrun:container", "free_items\n"));

    if (NULL == container) {
        snmp_log(LOG_ERR, "invalid container for netsnmp_swrun_container_free_items\n");
        return;
    }

    /*
     * free all items.
     */
    CONTAINER_CLEAR(container, _swrun_entry_release, NULL);
}

/**---------------------------------------------------------------------*/
/*
 * swrun_entry functions
 */
/**
 */
netsnmp_swrun_entry *
netsnmp_swrun_entry_get_by_index(netsnmp_container *container, oid index)
{
    netsnmp_index   tmp;

    DEBUGMSGTL(("swrun:entry", "by_index\n"));
    netsnmp_assert(1 == _swrun_init);

    if (NULL == container) {
        snmp_log(LOG_ERR,
                 "invalid container for netsnmp_swrun_entry_get_by_index\n");
        return NULL;
    }

    tmp.len = 1;
    tmp.oids = &index;

    return (netsnmp_swrun_entry *) CONTAINER_FIND(container, &tmp);
}

/**
 */
netsnmp_swrun_entry *
netsnmp_swrun_entry_create(int32_t index)
{
    netsnmp_swrun_entry *entry =
        SNMP_MALLOC_TYPEDEF(netsnmp_swrun_entry);

    if(NULL == entry)
        return NULL;

    entry->hrSWRunIndex = index;
    entry->hrSWRunType = 1; /* unknown */
    entry->hrSWRunStatus = 2; /* runnable */

    entry->oid_index.len = 1;
    entry->oid_index.oids = (oid *) & entry->hrSWRunIndex;

    return entry;
}

/**
 */
NETSNMP_INLINE void
netsnmp_swrun_entry_free(netsnmp_swrun_entry * entry)
{
    if (NULL == entry)
        return;

    /*
     * SNMP_FREE not needed, for any of these, 
     * since the whole entry is about to be freed
     */
    free(entry);
}

/**---------------------------------------------------------------------*/
/*
 * Utility routines
 */

/**
 */
static void
_swrun_entry_release(void *entry, void *context)
{
    netsnmp_swrun_entry_free(entry);
}


#ifdef TEST
int main(int argc, char *argv[])
{
    const char *tokens = getenv("SNMP_DEBUG");

    netsnmp_container_init_list();

    /** swrun,verbose:swrun,9:swrun,8:swrun,5:swrun */
    if (tokens)
        debug_register_tokens(tokens);
    else
        debug_register_tokens("swrun");
    snmp_set_do_debugging(1);

    init_swrun();
    netsnmp_swrun_container_load(NULL, 0);
    shutdown_swrun();

    return 0;
}
#endif
