4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Error handling support for directory lookup.
29 * Actually, this is intended to be a very generic and extensible error
30 * reporting mechanism.
42 #include <idmap_impl.h>
43 #include <rpcsvc/idmap_prot.h>
45 #include "directory.h"
48 * This is the actual implementation of the opaque directory_error_t structure.
50 struct directory_error
{
52 * True if this directory_error_t is statically allocated. Used to
53 * handle out of memory errors during error reporting.
58 * The error code. This is a locale-independent string that
59 * represents the precise error (to some level of granularity)
60 * that occurred. Internationalization processing could map it
61 * to an message. Errors may be subclassed by appending a dot
62 * and a name for the subclass.
64 * Note that this code plus the parameters allows for structured
65 * processing of error results.
70 * The default (in the absence of internationalization) format for
71 * the error message. %n interposes params[n - 1].
76 * Parameters to the error message. Note that subclasses are
77 * required to have the same initial parameters as their superclasses,
78 * so that code that processes the superclass can work on the subclass.
84 * Cached printable form (that is, with params[] interpolated into
85 * fmt) of the error message. Created when requested.
90 static directory_error_t
directory_error_internal_error(int err
);
93 * For debugging, reference count of directory_error instances still in
94 * existence. When the system is idle, this should be zero.
95 * Note that no attempt is made to make this MT safe, so it is not reliable
96 * in an MT environment.
98 static int directory_errors_outstanding
= 0;
101 * Free the specified directory_error_t. Note that this invalidates all strings
102 * returned based on it.
104 * Does nothing when de==NULL.
107 directory_error_free(directory_error_t de
)
114 /* Don't free our internal static directory_error_ts! */
123 /* Free parameters, if any */
124 if (de
->params
!= NULL
) {
125 for (i
= 0; i
< de
->nparams
; i
++) {
127 de
->params
[i
] = NULL
;
133 /* Free cached printable */
135 de
->printable
= NULL
;
139 directory_errors_outstanding
--;
143 * de = directory_error(code, fmt [, arg1 ... ]);
144 * Code, fmt, and arguments must be strings and will be copied.
147 directory_error(const char *code
, const char *fmt
, ...)
149 directory_error_t de
= NULL
;
153 de
= calloc(1, sizeof (*de
));
157 directory_errors_outstanding
++;
159 de
->is_static
= B_FALSE
;
161 de
->code
= strdup(code
);
162 if (de
->code
== NULL
)
165 de
->fmt
= strdup(fmt
);
169 /* Count our parameters */
171 for (i
= 0; va_arg(va
, char *) != NULL
; i
++)
178 * Note that we do not copy the terminating NULL because we have
181 de
->params
= calloc(de
->nparams
, sizeof (char *));
182 if (de
->params
== NULL
)
186 for (i
= 0; i
< de
->nparams
; i
++) {
187 de
->params
[i
] = strdup((char *)va_arg(va
, char *));
188 if (de
->params
[i
] == NULL
) {
199 directory_error_free(de
);
200 return (directory_error_internal_error(err
));
204 * Transform a directory_error returned by RPC into a directory_error_t.
207 directory_error_from_rpc(directory_error_rpc
*de_rpc
)
209 directory_error_t de
;
212 de
= calloc(1, sizeof (*de
));
216 directory_errors_outstanding
++;
218 de
->is_static
= B_FALSE
;
219 de
->code
= strdup(de_rpc
->code
);
220 if (de
->code
== NULL
)
222 de
->fmt
= strdup(de_rpc
->fmt
);
226 de
->nparams
= de_rpc
->params
.params_len
;
228 de
->params
= calloc(de
->nparams
, sizeof (char *));
229 if (de
->params
== NULL
)
232 for (i
= 0; i
< de
->nparams
; i
++) {
233 de
->params
[i
] = strdup(de_rpc
->params
.params_val
[i
]);
234 if (de
->params
[i
] == NULL
)
242 directory_error_free(de
);
243 return (directory_error_internal_error(err
));
247 * Convert a directory_error_t into a directory_error to send over RPC.
249 * Returns TRUE on successful conversion, FALSE on failure.
251 * Frees the directory_error_t.
253 * Note that most functions in this suite return boolean_t, as defined
254 * by types.h. This function is intended to be used directly as the
255 * return value from an RPC service function, and so it returns bool_t.
258 directory_error_to_rpc(directory_error_rpc
*de_rpc
, directory_error_t de
)
261 idmap_utf8str
*params
;
263 de_rpc
->code
= strdup(de
->code
);
264 if (de_rpc
->code
== NULL
)
267 de_rpc
->fmt
= strdup(de
->fmt
);
268 if (de_rpc
->fmt
== NULL
)
271 params
= calloc(de
->nparams
, sizeof (idmap_utf8str
));
274 de_rpc
->params
.params_val
= params
;
275 de_rpc
->params
.params_len
= de
->nparams
;
277 for (i
= 0; i
< de
->nparams
; i
++) {
278 params
[i
] = strdup(de
->params
[i
]);
279 if (params
[i
] == NULL
)
283 directory_error_free(de
);
287 logger(LOG_ERR
, "Warning: failed to convert error for RPC\n"
288 "Original error: %s\n"
289 "Conversion error: %s\n",
291 directory_error_printable(de
));
292 directory_error_free(de
);
297 * Determines whether this directory_error_t is an instance of the
298 * particular error, or a subclass of that error.
301 directory_error_is_instance_of(directory_error_t de
, char *code
)
305 if (de
== NULL
|| de
->code
== NULL
)
310 if (strncasecmp(de
->code
, code
, len
) != 0)
313 if (de
->code
[len
] == '\0' || de
->code
[len
] == '.')
320 * Expand the directory_error_t in de into buf, returning the size of the
321 * resulting string including terminating \0. If buf is NULL, just
324 * Return -1 if there are no substitutions, so that the caller can
325 * avoid memory allocation.
329 directory_error_expand(char *buf
, directory_error_t de
)
342 for (p
= dgettext(TEXT_DOMAIN
, de
->fmt
); *p
!= '\0'; ) {
347 n
= strtol(p
, &newp
, 10);
349 if (de
->params
== NULL
||
352 s
= dgettext(TEXT_DOMAIN
, "(missing)");
354 s
= de
->params
[n
- 1];
356 (void) strcpy(buf
+ bufsiz
, s
);
370 return (has_subst
? bufsiz
: -1);
374 * Returns a printable version of this directory_error_t, suitable for
377 * The value returned is valid as long as the directory_error_t is valid,
378 * and is freed when the directory_error_t is freed.
381 directory_error_printable(directory_error_t de
)
386 if (de
->printable
!= NULL
)
387 return (de
->printable
);
389 bufsiz
= directory_error_expand(NULL
, de
);
392 * Short circuit case to avoid memory allocation when there is
393 * no parameter substitution.
396 return (dgettext(TEXT_DOMAIN
, de
->fmt
));
400 return (dgettext(TEXT_DOMAIN
,
401 "Out of memory while expanding directory_error_t"));
404 (void) directory_error_expand(s
, de
);
407 * Stash the expansion away for later free, and to short-circuit
408 * repeated expansions.
412 return (de
->printable
);
416 * Returns the error code for the particular error, as a string.
417 * Note that this function should not normally be used to answer
418 * the question "did error X happen", since the value returned
419 * could be a subclass of X. directory_error_is_instance_of is intended
420 * to answer that question.
422 * The value returned is valid as long as the directory_error_t is valid,
423 * and is freed when the directory_error_t is freed.
426 directory_error_code(directory_error_t de
)
432 * Returns one of the parameters of the directory_error_t, or NULL if
433 * the parameter does not exist.
435 * Note that it is required that error subclasses have initial parameters
436 * the same as their superclasses.
438 * The value returned is valid as long as the directory_error_t is valid,
439 * and is freed when the directory_error_t is freed.
442 directory_error_param(directory_error_t de
, int param
)
444 if (param
>= de
->nparams
)
446 return (de
->params
[param
]);
450 * Here are some (almost) constant directory_error_t structures
451 * for use in reporting errors encountered while creating a
452 * directory_error_t structure. Unfortunately, the original error
455 #define gettext(x) x /* let xgettext see these messages */
456 static struct directory_error directory_error_ENOMEM
= {
458 "ENOMEM.directory_error_t",
459 gettext("Out of memory while creating a directory_error_t"),
464 static struct directory_error directory_error_EAGAIN
= {
466 "EAGAIN.directory_error_t",
467 gettext("Out of resources while creating a directory_error_t"),
472 /* 40 is big enough for even 128 bits */
473 static char directory_error_unknown_errno
[40] = "0";
474 static char *directory_error_unknown_params
[] = {
475 directory_error_unknown_errno
477 static struct directory_error directory_error_unknown
= {
479 "Unknown.directory_error_t",
480 gettext("Unknown error (%1) while creating a directory_error_t"),
481 1, directory_error_unknown_params
,
488 directory_error_internal_error(int err
)
491 case ENOMEM
: return (&directory_error_ENOMEM
);
492 case EAGAIN
: return (&directory_error_EAGAIN
);
494 /* Pray that we don't have a reentrancy problem ... */
495 (void) sprintf(directory_error_unknown_errno
, "%u", err
);
496 return (&directory_error_unknown
);