#include <stdio.h>
#include <stdlib.h>
#include <thread.h>
#include <errno.h>
#include <stdarg.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>
#include <syslog.h>
#include <idmap_impl.h>
#include <rpcsvc/idmap_prot.h>
#include <libintl.h>
#include "directory.h"
struct directory_error {
boolean_t is_static;
char *code;
char *fmt;
int nparams;
char **params;
char *printable;
};
static directory_error_t directory_error_internal_error(int err);
static int directory_errors_outstanding = 0;
void
directory_error_free(directory_error_t de)
{
int i;
if (de == NULL)
return;
if (de->is_static)
return;
free(de->code);
de->code = NULL;
free(de->fmt);
de->fmt = NULL;
if (de->params != NULL) {
for (i = 0; i < de->nparams; i++) {
free(de->params[i]);
de->params[i] = NULL;
}
free(de->params);
de->params = NULL;
}
free(de->printable);
de->printable = NULL;
free(de);
directory_errors_outstanding--;
}
directory_error_t
directory_error(const char *code, const char *fmt, ...)
{
directory_error_t de = NULL;
va_list va;
int i;
de = calloc(1, sizeof (*de));
if (de == NULL)
goto nomem;
directory_errors_outstanding++;
de->is_static = B_FALSE;
de->code = strdup(code);
if (de->code == NULL)
goto nomem;
de->fmt = strdup(fmt);
if (de->fmt == NULL)
goto nomem;
va_start(va, fmt);
for (i = 0; va_arg(va, char *) != NULL; i++)
;
va_end(va);
de->nparams = i;
de->params = calloc(de->nparams, sizeof (char *));
if (de->params == NULL)
goto nomem;
va_start(va, fmt);
for (i = 0; i < de->nparams; i++) {
de->params[i] = strdup((char *)va_arg(va, char *));
if (de->params[i] == NULL) {
va_end(va);
goto nomem;
}
}
va_end(va);
return (de);
nomem:;
int err = errno;
directory_error_free(de);
return (directory_error_internal_error(err));
}
directory_error_t
directory_error_from_rpc(directory_error_rpc *de_rpc)
{
directory_error_t de;
int i;
de = calloc(1, sizeof (*de));
if (de == NULL)
goto nomem;
directory_errors_outstanding++;
de->is_static = B_FALSE;
de->code = strdup(de_rpc->code);
if (de->code == NULL)
goto nomem;
de->fmt = strdup(de_rpc->fmt);
if (de->fmt == NULL)
goto nomem;
de->nparams = de_rpc->params.params_len;
de->params = calloc(de->nparams, sizeof (char *));
if (de->params == NULL)
goto nomem;
for (i = 0; i < de->nparams; i++) {
de->params[i] = strdup(de_rpc->params.params_val[i]);
if (de->params[i] == NULL)
goto nomem;
}
return (de);
nomem:;
int err = errno;
directory_error_free(de);
return (directory_error_internal_error(err));
}
bool_t
directory_error_to_rpc(directory_error_rpc *de_rpc, directory_error_t de)
{
int i;
idmap_utf8str *params;
de_rpc->code = strdup(de->code);
if (de_rpc->code == NULL)
goto nomem;
de_rpc->fmt = strdup(de->fmt);
if (de_rpc->fmt == NULL)
goto nomem;
params = calloc(de->nparams, sizeof (idmap_utf8str));
if (params == NULL)
goto nomem;
de_rpc->params.params_val = params;
de_rpc->params.params_len = de->nparams;
for (i = 0; i < de->nparams; i++) {
params[i] = strdup(de->params[i]);
if (params[i] == NULL)
goto nomem;
}
directory_error_free(de);
return (TRUE);
nomem:
logger(LOG_ERR, "Warning: failed to convert error for RPC\n"
"Original error: %s\n"
"Conversion error: %s\n",
strerror(errno),
directory_error_printable(de));
directory_error_free(de);
return (FALSE);
}
boolean_t
directory_error_is_instance_of(directory_error_t de, char *code)
{
int len;
if (de == NULL || de->code == NULL)
return (B_FALSE);
len = strlen(code);
if (strncasecmp(de->code, code, len) != 0)
return (B_FALSE);
if (de->code[len] == '\0' || de->code[len] == '.')
return (B_TRUE);
return (B_FALSE);
}
static
int
directory_error_expand(char *buf, directory_error_t de)
{
int bufsiz;
boolean_t has_subst;
const char *p;
char c;
long n;
const char *s;
char *newp;
bufsiz = 0;
has_subst = B_FALSE;
for (p = dgettext(TEXT_DOMAIN, de->fmt); *p != '\0'; ) {
c = *p++;
if (c == '%') {
has_subst = B_TRUE;
if (isdigit(*p)) {
n = strtol(p, &newp, 10);
p = newp;
if (de->params == NULL ||
n < 1 ||
n > de->nparams)
s = dgettext(TEXT_DOMAIN, "(missing)");
else
s = de->params[n - 1];
if (buf != NULL)
(void) strcpy(buf + bufsiz, s);
bufsiz += strlen(s);
continue;
}
}
if (buf != NULL)
buf[bufsiz] = c;
bufsiz++;
}
if (buf != NULL)
buf[bufsiz] = '\0';
bufsiz++;
return (has_subst ? bufsiz : -1);
}
const char *
directory_error_printable(directory_error_t de)
{
char *s;
int bufsiz;
if (de->printable != NULL)
return (de->printable);
bufsiz = directory_error_expand(NULL, de);
if (bufsiz < 0)
return (dgettext(TEXT_DOMAIN, de->fmt));
s = malloc(bufsiz);
if (s == NULL) {
return (dgettext(TEXT_DOMAIN,
"Out of memory while expanding directory_error_t"));
}
(void) directory_error_expand(s, de);
de->printable = s;
return (de->printable);
}
const char *
directory_error_code(directory_error_t de)
{
return (de->code);
}
const char *
directory_error_param(directory_error_t de, int param)
{
if (param >= de->nparams)
return (NULL);
return (de->params[param]);
}
#define gettext(x) x
static struct directory_error directory_error_ENOMEM = {
B_TRUE,
"ENOMEM.directory_error_t",
gettext("Out of memory while creating a directory_error_t"),
0, NULL,
NULL,
};
static struct directory_error directory_error_EAGAIN = {
B_TRUE,
"EAGAIN.directory_error_t",
gettext("Out of resources while creating a directory_error_t"),
0, NULL,
NULL,
};
static char directory_error_unknown_errno[40] = "0";
static char *directory_error_unknown_params[] = {
directory_error_unknown_errno
};
static struct directory_error directory_error_unknown = {
B_TRUE,
"Unknown.directory_error_t",
gettext("Unknown error (%1) while creating a directory_error_t"),
1, directory_error_unknown_params,
NULL,
};
#undef gettext
static
directory_error_t
directory_error_internal_error(int err)
{
switch (err) {
case ENOMEM: return (&directory_error_ENOMEM);
case EAGAIN: return (&directory_error_EAGAIN);
default:
(void) sprintf(directory_error_unknown_errno, "%u", err);
return (&directory_error_unknown);
}
}