root/usr/src/cmd/fm/modules/common/disk-monitor/util.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <smbios.h>

#include <fm/fmd_api.h>

#include "util.h"
#include "disk_monitor.h"

extern log_class_t g_verbose;

static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;

static void
verror(const char *fmt, va_list ap)
{
        int error = errno;

        dm_assert(pthread_mutex_lock(&log_mutex) == 0);
        fmd_hdl_vdebug(g_fm_hdl, fmt, ap);

        if (fmt[strlen(fmt) - 1] != '\n')
                fmd_hdl_debug(g_fm_hdl, ": %s\n", strerror(error));

        dm_assert(pthread_mutex_unlock(&log_mutex) == 0);
}

static void
vwarn_e(const char *fmt, va_list ap)
{
        int error = errno;

        dm_assert(pthread_mutex_lock(&log_mutex) == 0);
        fmd_hdl_debug(g_fm_hdl, "WARNING: ");
        fmd_hdl_vdebug(g_fm_hdl, fmt, ap);

        if (fmt[strlen(fmt) - 1] != '\n')
                fmd_hdl_debug(g_fm_hdl, ": %s\n", strerror(error));

        dm_assert(pthread_mutex_unlock(&log_mutex) == 0);
}

static void
vwarn(const char *fmt, va_list ap)
{
        dm_assert(pthread_mutex_lock(&log_mutex) == 0);
        fmd_hdl_debug(g_fm_hdl, "WARNING: ");
        fmd_hdl_vdebug(g_fm_hdl, fmt, ap);
        dm_assert(pthread_mutex_unlock(&log_mutex) == 0);
}

void
vcont(log_class_t cl, const char *fmt, va_list ap)
{
        int error = errno;

        if ((g_verbose & cl) != cl)
                return;

        dm_assert(pthread_mutex_lock(&log_mutex) == 0);
        fmd_hdl_vdebug(g_fm_hdl, fmt, ap);

        if (fmt[strlen(fmt) - 1] != '\n')
                fmd_hdl_debug(g_fm_hdl, ": %s\n", strerror(error));

        dm_assert(pthread_mutex_unlock(&log_mutex) == 0);
}

void
log_msg(log_class_t cl, const char *fmt, ...)
{
        va_list ap;

        if ((g_verbose & cl) != cl)
                return;

        dm_assert(pthread_mutex_lock(&log_mutex) == 0);
        va_start(ap, fmt);
        fmd_hdl_vdebug(g_fm_hdl, fmt, ap);
        va_end(ap);
        dm_assert(pthread_mutex_unlock(&log_mutex) == 0);
}

/*PRINTFLIKE1*/
void
log_err(const char *fmt, ...)
{
        va_list ap;

        if ((g_verbose & MM_ERR) != MM_ERR)
                return;

        va_start(ap, fmt);
        verror(fmt, ap);
        va_end(ap);
}

/*PRINTFLIKE1*/
void
log_warn(const char *fmt, ...)
{
        va_list ap;

        if ((g_verbose & MM_WARN) != MM_WARN)
                return;

        va_start(ap, fmt);
        vwarn(fmt, ap);
        va_end(ap);
}

/*PRINTFLIKE1*/
void
log_warn_e(const char *fmt, ...)
{
        va_list ap;

        if ((g_verbose & MM_WARN) != MM_WARN)
                return;

        va_start(ap, fmt);
        vwarn_e(fmt, ap);
        va_end(ap);
}

void
dfree(void *p, size_t sz)
{
        fmd_hdl_free(g_fm_hdl, p, sz);
}

void
dstrfree(char *s)
{
        fmd_hdl_strfree(g_fm_hdl, s);
}

void *
dmalloc(size_t sz)
{
        return (fmd_hdl_alloc(g_fm_hdl, sz, FMD_SLEEP));
}

void *
dzmalloc(size_t sz)
{
        return (fmd_hdl_zalloc(g_fm_hdl, sz, FMD_SLEEP));
}


char *
dstrdup(const char *s)
{
        return (fmd_hdl_strdup(g_fm_hdl, s, FMD_SLEEP));
}

void
queue_add(qu_t *qp, void *data)
{
        struct q_node *qnp =
            (struct q_node *)qp->nalloc(sizeof (struct q_node));
        struct q_node *nodep;

        qnp->data = data;
        qnp->next = NULL;
        dm_assert(pthread_mutex_lock(&qp->mutex) == 0);

        if (qp->nodep == NULL)
                qp->nodep = qnp;
        else {
                nodep = qp->nodep;

                while (nodep->next != NULL)
                        nodep = nodep->next;

                nodep->next = qnp;
        }

        /* If the queue was empty, we need to wake people up */
        if (qp->boe && qp->nodep == qnp)
                dm_assert(pthread_cond_broadcast(&qp->cvar) == 0);
        dm_assert(pthread_mutex_unlock(&qp->mutex) == 0);
}

void *
queue_remove(qu_t *qp)
{
        void *rv = NULL;
        struct q_node *nextnode;

        dm_assert(pthread_mutex_lock(&qp->mutex) == 0);

        /* Wait while the queue is empty */
        while (qp->boe && qp->nodep == NULL) {
                (void) pthread_cond_wait(&qp->cvar, &qp->mutex);
        }

        /*
         * If Block-On-Empty is false, the queue may be empty
         */
        if (qp->nodep != NULL) {
                rv = qp->nodep->data;
                nextnode = qp->nodep->next;
                qp->nfree(qp->nodep, sizeof (struct q_node));
                qp->nodep = nextnode;
        }

        dm_assert(pthread_mutex_unlock(&qp->mutex) == 0);
        return (rv);
}

qu_t *
new_queue(boolean_t block_on_empty, void *(*nodealloc)(size_t),
    void (*nodefree)(void *, size_t), void (*data_deallocator)(void *))
{
        qu_t *newqp = (qu_t *)dmalloc(sizeof (qu_t));

        newqp->boe = block_on_empty;
        newqp->nalloc = nodealloc;
        newqp->nfree = nodefree;
        newqp->data_dealloc = data_deallocator;
        dm_assert(pthread_mutex_init(&newqp->mutex, NULL) == 0);
        dm_assert(pthread_cond_init(&newqp->cvar, NULL) == 0);
        newqp->nodep = NULL;

        return (newqp);
}

void
queue_free(qu_t **qpp)
{
        qu_t *qp = *qpp;
        void *item;

        dm_assert(pthread_mutex_destroy(&qp->mutex) == 0);
        dm_assert(pthread_cond_destroy(&qp->cvar) == 0);

        qp->boe = B_FALSE;

        while ((item = queue_remove(qp)) != NULL) {
                qp->data_dealloc(item);
        }

        dm_assert(qp->nodep == NULL);

        dfree(qp, sizeof (qu_t));
        *qpp = NULL;
}

int
_dm_assert(const char *assertion, const char *file, int line, const char *func)
{
        /*
         * No newline is appended to the assertion message so that
         * errno can be translated for us by fmd_hdl_abort().
         */
        if (func)
                fmd_hdl_abort(g_fm_hdl, "Assertion failed: "
                    "%s, file: %s, line: %d, function: %s", assertion, file,
                    line, func);
        else
                fmd_hdl_abort(g_fm_hdl, "Assertion failed: "
                    "%s, file: %s, line: %d", assertion, file, line);
        /*NOTREACHED*/
        return (0);
}