2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/*
2N/A * Error handling support for directory lookup.
2N/A * Actually, this is intended to be a very generic and extensible error
2N/A * reporting mechanism.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <thread.h>
2N/A#include <errno.h>
2N/A#include <stdarg.h>
2N/A#include <malloc.h>
2N/A#include <string.h>
2N/A#include <ctype.h>
2N/A#include <syslog.h>
2N/A#include <idmap_impl.h>
2N/A#include <rpcsvc/idmap_prot.h>
2N/A#include <libintl.h>
2N/A#include "directory.h"
2N/A
2N/A/*
2N/A * This is the actual implementation of the opaque directory_error_t structure.
2N/A */
2N/Astruct directory_error {
2N/A /*
2N/A * True if this directory_error_t is statically allocated. Used to
2N/A * handle out of memory errors during error reporting.
2N/A */
2N/A boolean_t is_static;
2N/A
2N/A /*
2N/A * The error code. This is a locale-independent string that
2N/A * represents the precise error (to some level of granularity)
2N/A * that occurred. Internationalization processing could map it
2N/A * to an message. Errors may be subclassed by appending a dot
2N/A * and a name for the subclass.
2N/A *
2N/A * Note that this code plus the parameters allows for structured
2N/A * processing of error results.
2N/A */
2N/A char *code;
2N/A
2N/A /*
2N/A * The default (in the absence of internationalization) format for
2N/A * the error message. %n interposes params[n - 1].
2N/A */
2N/A char *fmt;
2N/A
2N/A /*
2N/A * Parameters to the error message. Note that subclasses are
2N/A * required to have the same initial parameters as their superclasses,
2N/A * so that code that processes the superclass can work on the subclass.
2N/A */
2N/A int nparams;
2N/A char **params;
2N/A
2N/A /*
2N/A * Cached printable form (that is, with params[] interpolated into
2N/A * fmt) of the error message. Created when requested.
2N/A */
2N/A char *printable;
2N/A};
2N/A
2N/Astatic directory_error_t directory_error_internal_error(int err);
2N/A
2N/A/*
2N/A * For debugging, reference count of directory_error instances still in
2N/A * existence. When the system is idle, this should be zero.
2N/A * Note that no attempt is made to make this MT safe, so it is not reliable
2N/A * in an MT environment.
2N/A */
2N/Astatic int directory_errors_outstanding = 0;
2N/A
2N/A/*
2N/A * Free the specified directory_error_t. Note that this invalidates all strings
2N/A * returned based on it.
2N/A *
2N/A * Does nothing when de==NULL.
2N/A */
2N/Avoid
2N/Adirectory_error_free(directory_error_t de)
2N/A{
2N/A int i;
2N/A
2N/A if (de == NULL)
2N/A return;
2N/A
2N/A /* Don't free our internal static directory_error_ts! */
2N/A if (de->is_static)
2N/A return;
2N/A
2N/A free(de->code);
2N/A de->code = NULL;
2N/A free(de->fmt);
2N/A de->fmt = NULL;
2N/A
2N/A /* Free parameters, if any */
2N/A if (de->params != NULL) {
2N/A for (i = 0; i < de->nparams; i++) {
2N/A free(de->params[i]);
2N/A de->params[i] = NULL;
2N/A }
2N/A free(de->params);
2N/A de->params = NULL;
2N/A }
2N/A
2N/A /* Free cached printable */
2N/A free(de->printable);
2N/A de->printable = NULL;
2N/A
2N/A free(de);
2N/A
2N/A directory_errors_outstanding--;
2N/A}
2N/A
2N/A/*
2N/A * de = directory_error(code, fmt [, arg1 ... ]);
2N/A * Code, fmt, and arguments must be strings and will be copied.
2N/A */
2N/Adirectory_error_t
2N/Adirectory_error(const char *code, const char *fmt, ...)
2N/A{
2N/A directory_error_t de = NULL;
2N/A va_list va;
2N/A int i;
2N/A
2N/A de = calloc(1, sizeof (*de));
2N/A if (de == NULL)
2N/A goto nomem;
2N/A
2N/A directory_errors_outstanding++;
2N/A
2N/A de->is_static = B_FALSE;
2N/A
2N/A de->code = strdup(code);
2N/A if (de->code == NULL)
2N/A goto nomem;
2N/A
2N/A de->fmt = strdup(fmt);
2N/A if (de->fmt == NULL)
2N/A goto nomem;
2N/A
2N/A /* Count our parameters */
2N/A va_start(va, fmt);
2N/A for (i = 0; va_arg(va, char *) != NULL; i++)
2N/A /* LOOP */;
2N/A va_end(va);
2N/A
2N/A de->nparams = i;
2N/A
2N/A /*
2N/A * Note that we do not copy the terminating NULL because we have
2N/A * a count.
2N/A */
2N/A de->params = calloc(de->nparams, sizeof (char *));
2N/A if (de->params == NULL)
2N/A goto nomem;
2N/A
2N/A va_start(va, fmt);
2N/A for (i = 0; i < de->nparams; i++) {
2N/A de->params[i] = strdup((char *)va_arg(va, char *));
2N/A if (de->params[i] == NULL) {
2N/A va_end(va);
2N/A goto nomem;
2N/A }
2N/A }
2N/A va_end(va);
2N/A
2N/A return (de);
2N/A
2N/Anomem:;
2N/A int err = errno;
2N/A directory_error_free(de);
2N/A return (directory_error_internal_error(err));
2N/A}
2N/A
2N/A/*
2N/A * Transform a directory_error returned by RPC into a directory_error_t.
2N/A */
2N/Adirectory_error_t
2N/Adirectory_error_from_rpc(directory_error_rpc *de_rpc)
2N/A{
2N/A directory_error_t de;
2N/A int i;
2N/A
2N/A de = calloc(1, sizeof (*de));
2N/A if (de == NULL)
2N/A goto nomem;
2N/A
2N/A directory_errors_outstanding++;
2N/A
2N/A de->is_static = B_FALSE;
2N/A de->code = strdup(de_rpc->code);
2N/A if (de->code == NULL)
2N/A goto nomem;
2N/A de->fmt = strdup(de_rpc->fmt);
2N/A if (de->fmt == NULL)
2N/A goto nomem;
2N/A
2N/A de->nparams = de_rpc->params.params_len;
2N/A
2N/A de->params = calloc(de->nparams, sizeof (char *));
2N/A if (de->params == NULL)
2N/A goto nomem;
2N/A
2N/A for (i = 0; i < de->nparams; i++) {
2N/A de->params[i] = strdup(de_rpc->params.params_val[i]);
2N/A if (de->params[i] == NULL)
2N/A goto nomem;
2N/A }
2N/A
2N/A return (de);
2N/A
2N/Anomem:;
2N/A int err = errno;
2N/A directory_error_free(de);
2N/A return (directory_error_internal_error(err));
2N/A}
2N/A
2N/A/*
2N/A * Convert a directory_error_t into a directory_error to send over RPC.
2N/A *
2N/A * Returns TRUE on successful conversion, FALSE on failure.
2N/A *
2N/A * Frees the directory_error_t.
2N/A *
2N/A * Note that most functions in this suite return boolean_t, as defined
2N/A * by types.h. This function is intended to be used directly as the
2N/A * return value from an RPC service function, and so it returns bool_t.
2N/A */
2N/Abool_t
2N/Adirectory_error_to_rpc(directory_error_rpc *de_rpc, directory_error_t de)
2N/A{
2N/A int i;
2N/A idmap_utf8str *params;
2N/A
2N/A de_rpc->code = strdup(de->code);
2N/A if (de_rpc->code == NULL)
2N/A goto nomem;
2N/A
2N/A de_rpc->fmt = strdup(de->fmt);
2N/A if (de_rpc->fmt == NULL)
2N/A goto nomem;
2N/A
2N/A params = calloc(de->nparams, sizeof (idmap_utf8str));
2N/A if (params == NULL)
2N/A goto nomem;
2N/A de_rpc->params.params_val = params;
2N/A de_rpc->params.params_len = de->nparams;
2N/A
2N/A for (i = 0; i < de->nparams; i++) {
2N/A params[i] = strdup(de->params[i]);
2N/A if (params[i] == NULL)
2N/A goto nomem;
2N/A }
2N/A
2N/A directory_error_free(de);
2N/A return (TRUE);
2N/A
2N/Anomem:
2N/A logger(LOG_ERR, "Warning: failed to convert error for RPC\n"
2N/A "Original error: %s\n"
2N/A "Conversion error: %s\n",
2N/A strerror(errno),
2N/A directory_error_printable(de));
2N/A directory_error_free(de);
2N/A return (FALSE);
2N/A}
2N/A
2N/A/*
2N/A * Determines whether this directory_error_t is an instance of the
2N/A * particular error, or a subclass of that error.
2N/A */
2N/Aboolean_t
2N/Adirectory_error_is_instance_of(directory_error_t de, char *code)
2N/A{
2N/A int len;
2N/A
2N/A if (de == NULL || de->code == NULL)
2N/A return (B_FALSE);
2N/A
2N/A len = strlen(code);
2N/A
2N/A if (strncasecmp(de->code, code, len) != 0)
2N/A return (B_FALSE);
2N/A
2N/A if (de->code[len] == '\0' || de->code[len] == '.')
2N/A return (B_TRUE);
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * Expand the directory_error_t in de into buf, returning the size of the
2N/A * resulting string including terminating \0. If buf is NULL, just
2N/A * return the size.
2N/A *
2N/A * Return -1 if there are no substitutions, so that the caller can
2N/A * avoid memory allocation.
2N/A */
2N/Astatic
2N/Aint
2N/Adirectory_error_expand(char *buf, directory_error_t de)
2N/A{
2N/A int bufsiz;
2N/A boolean_t has_subst;
2N/A const char *p;
2N/A char c;
2N/A long n;
2N/A const char *s;
2N/A char *newp;
2N/A
2N/A bufsiz = 0;
2N/A has_subst = B_FALSE;
2N/A
2N/A for (p = dgettext(TEXT_DOMAIN, de->fmt); *p != '\0'; ) {
2N/A c = *p++;
2N/A if (c == '%') {
2N/A has_subst = B_TRUE;
2N/A if (isdigit(*p)) {
2N/A n = strtol(p, &newp, 10);
2N/A p = newp;
2N/A if (de->params == NULL ||
2N/A n < 1 ||
2N/A n > de->nparams)
2N/A s = dgettext(TEXT_DOMAIN, "(missing)");
2N/A else
2N/A s = de->params[n - 1];
2N/A if (buf != NULL)
2N/A (void) strcpy(buf + bufsiz, s);
2N/A bufsiz += strlen(s);
2N/A continue;
2N/A }
2N/A }
2N/A if (buf != NULL)
2N/A buf[bufsiz] = c;
2N/A bufsiz++;
2N/A }
2N/A
2N/A if (buf != NULL)
2N/A buf[bufsiz] = '\0';
2N/A bufsiz++;
2N/A
2N/A return (has_subst ? bufsiz : -1);
2N/A}
2N/A
2N/A/*
2N/A * Returns a printable version of this directory_error_t, suitable for
2N/A * human consumption.
2N/A *
2N/A * The value returned is valid as long as the directory_error_t is valid,
2N/A * and is freed when the directory_error_t is freed.
2N/A */
2N/Aconst char *
2N/Adirectory_error_printable(directory_error_t de)
2N/A{
2N/A char *s;
2N/A int bufsiz;
2N/A
2N/A if (de->printable != NULL)
2N/A return (de->printable);
2N/A
2N/A bufsiz = directory_error_expand(NULL, de);
2N/A
2N/A /*
2N/A * Short circuit case to avoid memory allocation when there is
2N/A * no parameter substitution.
2N/A */
2N/A if (bufsiz < 0)
2N/A return (dgettext(TEXT_DOMAIN, de->fmt));
2N/A
2N/A s = malloc(bufsiz);
2N/A if (s == NULL) {
2N/A return (dgettext(TEXT_DOMAIN,
2N/A "Out of memory while expanding directory_error_t"));
2N/A }
2N/A
2N/A (void) directory_error_expand(s, de);
2N/A
2N/A /*
2N/A * Stash the expansion away for later free, and to short-circuit
2N/A * repeated expansions.
2N/A */
2N/A de->printable = s;
2N/A
2N/A return (de->printable);
2N/A}
2N/A
2N/A/*
2N/A * Returns the error code for the particular error, as a string.
2N/A * Note that this function should not normally be used to answer
2N/A * the question "did error X happen", since the value returned
2N/A * could be a subclass of X. directory_error_is_instance_of is intended
2N/A * to answer that question.
2N/A *
2N/A * The value returned is valid as long as the directory_error_t is valid,
2N/A * and is freed when the directory_error_t is freed.
2N/A */
2N/Aconst char *
2N/Adirectory_error_code(directory_error_t de)
2N/A{
2N/A return (de->code);
2N/A}
2N/A
2N/A/*
2N/A * Returns one of the parameters of the directory_error_t, or NULL if
2N/A * the parameter does not exist.
2N/A *
2N/A * Note that it is required that error subclasses have initial parameters
2N/A * the same as their superclasses.
2N/A *
2N/A * The value returned is valid as long as the directory_error_t is valid,
2N/A * and is freed when the directory_error_t is freed.
2N/A */
2N/Aconst char *
2N/Adirectory_error_param(directory_error_t de, int param)
2N/A{
2N/A if (param >= de->nparams)
2N/A return (NULL);
2N/A return (de->params[param]);
2N/A}
2N/A
2N/A/*
2N/A * Here are some (almost) constant directory_error_t structures
2N/A * for use in reporting errors encountered while creating a
2N/A * directory_error_t structure. Unfortunately, the original error
2N/A * report is lost.
2N/A */
2N/A#define gettext(x) x /* let xgettext see these messages */
2N/Astatic struct directory_error directory_error_ENOMEM = {
2N/A B_TRUE,
2N/A "ENOMEM.directory_error_t",
2N/A gettext("Out of memory while creating a directory_error_t"),
2N/A 0, NULL,
2N/A NULL,
2N/A};
2N/A
2N/Astatic struct directory_error directory_error_EAGAIN = {
2N/A B_TRUE,
2N/A "EAGAIN.directory_error_t",
2N/A gettext("Out of resources while creating a directory_error_t"),
2N/A 0, NULL,
2N/A NULL,
2N/A};
2N/A
2N/A/* 40 is big enough for even 128 bits */
2N/Astatic char directory_error_unknown_errno[40] = "0";
2N/Astatic char *directory_error_unknown_params[] = {
2N/A directory_error_unknown_errno
2N/A};
2N/Astatic struct directory_error directory_error_unknown = {
2N/A B_TRUE,
2N/A "Unknown.directory_error_t",
2N/A gettext("Unknown error (%1) while creating a directory_error_t"),
2N/A 1, directory_error_unknown_params,
2N/A NULL,
2N/A};
2N/A#undef gettext
2N/A
2N/Astatic
2N/Adirectory_error_t
2N/Adirectory_error_internal_error(int err)
2N/A{
2N/A switch (err) {
2N/A case ENOMEM: return (&directory_error_ENOMEM);
2N/A case EAGAIN: return (&directory_error_EAGAIN);
2N/A default:
2N/A /* Pray that we don't have a reentrancy problem ... */
2N/A (void) sprintf(directory_error_unknown_errno, "%u", err);
2N/A return (&directory_error_unknown);
2N/A }
2N/A}