root/usr/src/cmd/logadm/err.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 (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * logadm/err.c -- some basic error routines
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <libintl.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include "err.h"

jmp_buf Err_env;
jmp_buf *Err_env_ptr;
static const char *Myname;
static int Exitcode;
static FILE *Errorfile;

/*
 * err_init -- initialize the error handling routine
 *
 */
void
err_init(const char *myname)
{
        char *ptr;

        if ((ptr = strrchr(myname, '/')) == NULL)
                Myname = myname;
        else
                Myname = ptr + 1;
}

static const char *File;
static int Line;

/*
 * err_fileline -- record the filename/line number for err(EF_FILE, ...)
 */
void
err_fileline(const char *file, int line)
{
        File = file;
        Line = line;
}

/*
 * err -- print an error message and return, exit or longjmp based on flags
 *
 * this routine calls gettext() to translate the fmt string.
 */
/*PRINTFLIKE2*/
void
err(int flags, const char *fmt, ...)
{
        va_list ap;
        int safe_errno = errno;
        char *errno_msg = NULL;
        int as_is = 0;
        int jump = 0;
        int warning = 0;
        int fileline = 0;
        char *prefix = "Error: ";
        const char *intlfmt;

        va_start(ap, fmt);
        intlfmt = gettext(fmt);

        if (flags & EF_WARN) {
                warning = 1;
                prefix = "Warning: ";
        }
        if (flags & EF_FILE) {
                fileline = 1;
                Exitcode++;
        }
        if (flags & EF_SYS)
                errno_msg = strerror(safe_errno);
        if (flags & EF_JMP)
                jump = 1;
        if (flags & EF_RAW)
                as_is = 1;

        /* print a copy to stderr */
        if (!as_is) {
                if (Myname != NULL) {
                        (void) fprintf(stderr, "%s: ", Myname);
                        if (Errorfile)
                                (void) fprintf(Errorfile, "%s: ", Myname);
                }
                if (fileline && File) {
                        (void) fprintf(stderr, "%s line %d: ", File, Line);
                        if (Errorfile)
                                (void) fprintf(Errorfile,
                                    "%s line %d: ", File, Line);
                }
                (void) fputs(gettext(prefix), stderr);
                if (Errorfile)
                        (void) fputs(gettext(prefix), Errorfile);
        }
        (void) vfprintf(stderr, intlfmt, ap);
        if (Errorfile)
                (void) vfprintf(Errorfile, intlfmt, ap);
        if (errno_msg != NULL) {
                (void) fprintf(stderr, ": %s", errno_msg);
                if (Errorfile)
                        (void) fprintf(Errorfile, ": %s", errno_msg);
        }
        if (!as_is) {
                (void) fprintf(stderr, "\n");
                if (Errorfile)
                        (void) fprintf(Errorfile, "\n");
        }
        (void) fflush(stderr);
        if (Errorfile)
                (void) fflush(Errorfile);

        va_end(ap);

        if (jump)
                longjmp(*Err_env_ptr, 1);

        if (!warning && !fileline) {
                err_done(1);
                /*NOTREACHED*/
        }
}

/*
 * out -- print a message and return
 *
 * this routine calls gettext() to translate the fmt string.
 */
/*PRINTFLIKE1*/
void
out(const char *fmt, ...)
{
        va_list ap;

        va_start(ap, fmt);

        (void) vfprintf(stdout, gettext(fmt), ap);

        va_end(ap);
}

#define CHUNKSIZE 8192          /* for copying stderr */
/*
 * err_fromfd -- copy data from fd to stderr
 */
void
err_fromfd(int fd)
{
        char buf[CHUNKSIZE];
        int count;

        while ((count = read(fd, buf, CHUNKSIZE)) > 0) {
                (void) fwrite(buf, 1, count, stderr);
                if (Errorfile)
                        (void) fwrite(buf, 1, count, Errorfile);
        }
        (void) fflush(stderr);
        if (Errorfile)
                (void) fflush(Errorfile);
}

/*
 * err_done -- exit the program
 */
void
err_done(int exitcode)
{
        /* send error mail if applicable */
        err_mailto(NULL);

        if (exitcode)
                exit(exitcode);
        else
                exit(Exitcode);
        /*NOTREACHED*/
}

#define MAXLINE 8192    /* for tmp file line buffer */
/*
 * err_mailto -- arrange for error output to be mailed to someone
 */
void
err_mailto(const char *recipient)
{
        static const char *lastrecipient;
        static char mailcmd[] = "/bin/mailx -s 'logadm error output'";
        char *cmd;
        int len;
        FILE *pfp;
        char line[MAXLINE];

        if (lastrecipient != NULL) {
                if (recipient != NULL &&
                    strcmp(recipient, lastrecipient) == 0)
                        return;         /* keep going, same recipient */

                /* stop saving output for lastrecipient and send message */
                if (ftell(Errorfile)) {
                        rewind(Errorfile);
                        len = strlen(lastrecipient) + strlen(mailcmd) + 2;
                        cmd = MALLOC(len);
                        (void) snprintf(cmd, len, "%s %s",
                            mailcmd, lastrecipient);
                        if ((pfp = popen(cmd, "w")) == NULL)
                                err(EF_SYS, "popen to mailx");
                        while (fgets(line, MAXLINE, Errorfile) != NULL)
                                (void) fputs(line, pfp);
                        (void) pclose(pfp);
                }
                (void) fclose(Errorfile);
                Errorfile = NULL;
        }

        if (recipient != NULL) {
                /* start saving error output for this recipient */
                if ((Errorfile = tmpfile()) == NULL)
                        err(EF_SYS, "tmpfile");
        }
        lastrecipient = recipient;
}

/*
 * err_malloc -- a malloc() with checks
 *
 * this routine is typically called via the MALLOC() macro in err.h
 */
void *
err_malloc(int nbytes, const char *fname, int line)
{
        void *retval = malloc(nbytes);

        if (retval == NULL)
                err(0, "%s:%d: out of memory", fname, line);

        return (retval);
}

/*
 * err_realloc -- a realloc() with checks
 *
 * this routine is typically called via the REALLOC() macro in err.h
 */
void *
err_realloc(void *ptr, int nbytes, const char *fname, int line)
{
        void *retval = realloc(ptr, nbytes);

        if (retval == NULL)
                err(0, "%s:%d: out of memory", fname, line);

        return (retval);
}

/*
 * err_strdup -- a strdup() with checks
 *
 * this routine is typically called via the STRDUP() macro in err.h
 */
char *
err_strdup(const char *ptr, const char *fname, int line)
{
        char *retval = NULL;

        if (ptr != NULL) {
                retval = strdup(ptr);
                if (retval == NULL)
                        err(0, "%s:%d: out of memory", fname, line);
        } else
                err(0, "%s:%d: could not strdup", fname, line);


        return (retval);

}

/*
 * err_free -- a free() with checks
 *
 * this routine is typically called via the FREE() macro in err.h
 */
/*ARGSUSED1*/
void
err_free(void *ptr, const char *fname, int line)
{
        /* nothing to check in this version */
        free(ptr);
}

/*
 * err_exitcode -- set an error exit code for when done(0) is called
 */
void
err_exitcode(int exitcode)
{
        Exitcode = exitcode;
}