/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <fmd_alloc.h>
#include <fmd_subr.h>
#include <fmd_error.h>
#include <fmd_string.h>
#include <fmd_scheme.h>
#include <fmd_fmri.h>
#include <fmd_topo.h>
#include <fmd.h>
/*
* Interfaces to be used by the plugins
*/
void *
fmd_fmri_alloc(size_t size)
{
return (fmd_alloc(size, FMD_SLEEP));
}
void *
fmd_fmri_zalloc(size_t size)
{
return (fmd_zalloc(size, FMD_SLEEP));
}
void
fmd_fmri_free(void *data, size_t size)
{
fmd_free(data, size);
}
int
fmd_fmri_set_errno(int err)
{
errno = err;
return (-1);
}
void
fmd_fmri_warn(const char *format, ...)
{
va_list ap;
va_start(ap, format);
fmd_verror(EFMD_FMRI_SCHEME, format, ap);
va_end(ap);
}
/*
* Convert an input string to a URI escaped string and return the new string.
* RFC2396 Section 2.4 says that data must be escaped if it does not have a
* representation using an unreserved character, where an unreserved character
* is one that is either alphanumeric or one of the marks defined in S2.3.
*/
static size_t
fmd_fmri_uriescape(const char *s, const char *xmark, char *buf, size_t len)
{
static const char rfc2396_mark[] = "-_.!~*'()";
static const char hex_digits[] = "0123456789ABCDEF";
static const char empty_str[] = "";
const char *p;
char c, *q;
size_t n = 0;
if (s == NULL)
s = empty_str;
if (xmark == NULL)
xmark = empty_str;
for (p = s; (c = *p) != '\0'; p++) {
if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c))
n++; /* represent c as itself */
else
n += 3; /* represent c as escape */
}
if (buf == NULL)
return (n);
for (p = s, q = buf; (c = *p) != '\0' && q < buf + len; p++) {
if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c)) {
*q++ = c;
} else {
*q++ = '%';
*q++ = hex_digits[((uchar_t)c & 0xf0) >> 4];
*q++ = hex_digits[(uchar_t)c & 0xf];
}
}
if (q == buf + len)
q--; /* len is too small: truncate output string */
*q = '\0';
return (n);
}
/*
* Convert a name-value pair list representing an FMRI authority into the
* corresponding RFC2396 string representation and return the new string.
*/
char *
fmd_fmri_auth2str(nvlist_t *nvl)
{
nvpair_t *nvp;
char *s, *p, *v;
size_t n = 0;
for (nvp = nvlist_next_nvpair(nvl, NULL);
nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) {
if (nvpair_type(nvp) != DATA_TYPE_STRING)
continue; /* do not format non-string elements */
n += fmd_fmri_uriescape(nvpair_name(nvp), NULL, NULL, 0) + 1;
(void) nvpair_value_string(nvp, &v);
n += fmd_fmri_uriescape(v, ":", NULL, 0) + 1;
}
p = s = fmd_alloc(n, FMD_SLEEP);
for (nvp = nvlist_next_nvpair(nvl, NULL);
nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) {
if (nvpair_type(nvp) != DATA_TYPE_STRING)
continue; /* do not format non-string elements */
if (p != s)
*p++ = ',';
p += fmd_fmri_uriescape(nvpair_name(nvp), NULL, p, n);
*p++ = '=';
(void) nvpair_value_string(nvp, &v);
p += fmd_fmri_uriescape(v, ":", p, n);
}
return (s);
}
/*
* Convert an input string to a URI escaped string and return the new string.
* We amend the unreserved character list to include commas and colons,
* as both are needed to make FMRIs readable without escaping. We also permit
* "/" to pass through unescaped as any path delimiters used by the event
* creator are presumably intended to appear in the final path.
*/
char *
fmd_fmri_strescape(const char *s)
{
char *s2;
size_t n;
if (s == NULL)
return (NULL);
n = fmd_fmri_uriescape(s, ":,/", NULL, 0);
s2 = fmd_alloc(n + 1, FMD_SLEEP);
(void) fmd_fmri_uriescape(s, ":,/", s2, n + 1);
return (s2);
}
char *
fmd_fmri_strdup(const char *s)
{
return (fmd_strdup(s, FMD_SLEEP));
}
void
fmd_fmri_strfree(char *s)
{
fmd_strfree(s);
}
const char *
fmd_fmri_get_rootdir(void)
{
return (fmd.d_rootdir);
}
const char *
fmd_fmri_get_platform(void)
{
return (fmd.d_platform);
}
uint64_t
fmd_fmri_get_drgen(void)
{
uint64_t gen;
(void) pthread_mutex_lock(&fmd.d_stats_lock);
gen = fmd.d_stats->ds_dr_gen.fmds_value.ui64;
(void) pthread_mutex_unlock(&fmd.d_stats_lock);
return (gen);
}
struct topo_hdl *
fmd_fmri_topo_hold(int version)
{
fmd_topo_t *ftp;
if (version != TOPO_VERSION)
return (NULL);
ftp = fmd_topo_hold();
return (ftp->ft_hdl);
}
void
fmd_fmri_topo_rele(struct topo_hdl *thp)
{
fmd_topo_rele_hdl(thp);
}
/*
* Interfaces for users of the plugins
*/
static fmd_scheme_t *
nvl2scheme(nvlist_t *nvl)
{
char *name;
if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) {
(void) fmd_set_errno(EFMD_FMRI_INVAL);
return (NULL);
}
return (fmd_scheme_hash_lookup(fmd.d_schemes, name));
}
ssize_t
fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
{
fmd_scheme_t *sp;
char c;
ssize_t rv;
if (buf == NULL && buflen == 0) {
buf = &c;
buflen = sizeof (c);
}
if ((sp = nvl2scheme(nvl)) == NULL)
return (-1); /* errno is set for us */
(void) pthread_mutex_lock(&sp->sch_opslock);
ASSERT(buf != NULL || buflen == 0);
rv = sp->sch_ops.sop_nvl2str(nvl, buf, buflen);
(void) pthread_mutex_unlock(&sp->sch_opslock);
fmd_scheme_hash_release(fmd.d_schemes, sp);
return (rv);
}
int
fmd_fmri_expand(nvlist_t *nvl)
{
fmd_scheme_t *sp;
int rv;
if ((sp = nvl2scheme(nvl)) == NULL)
return (-1); /* errno is set for us */
(void) pthread_mutex_lock(&sp->sch_opslock);
rv = sp->sch_ops.sop_expand(nvl);
(void) pthread_mutex_unlock(&sp->sch_opslock);
fmd_scheme_hash_release(fmd.d_schemes, sp);
return (rv);
}
int
fmd_fmri_present(nvlist_t *nvl)
{
fmd_scheme_t *sp;
int rv;
if ((sp = nvl2scheme(nvl)) == NULL)
return (-1); /* errno is set for us */
(void) pthread_mutex_lock(&sp->sch_opslock);
rv = sp->sch_ops.sop_present(nvl);
(void) pthread_mutex_unlock(&sp->sch_opslock);
fmd_scheme_hash_release(fmd.d_schemes, sp);
return (rv);
}
int
fmd_fmri_replaced(nvlist_t *nvl)
{
fmd_scheme_t *sp;
int rv;
if ((sp = nvl2scheme(nvl)) == NULL)
return (-1); /* errno is set for us */
(void) pthread_mutex_lock(&sp->sch_opslock);
rv = sp->sch_ops.sop_replaced(nvl);
(void) pthread_mutex_unlock(&sp->sch_opslock);
fmd_scheme_hash_release(fmd.d_schemes, sp);
return (rv);
}
int
fmd_fmri_service_state(nvlist_t *nvl)
{
fmd_scheme_t *sp;
int rv;
if ((sp = nvl2scheme(nvl)) == NULL)
return (-1); /* errno is set for us */
(void) pthread_mutex_lock(&sp->sch_opslock);
rv = sp->sch_ops.sop_service_state(nvl);
(void) pthread_mutex_unlock(&sp->sch_opslock);
fmd_scheme_hash_release(fmd.d_schemes, sp);
return (rv);
}
int
fmd_fmri_unusable(nvlist_t *nvl)
{
fmd_scheme_t *sp;
int rv;
if ((sp = nvl2scheme(nvl)) == NULL)
return (-1); /* errno is set for us */
(void) pthread_mutex_lock(&sp->sch_opslock);
rv = sp->sch_ops.sop_unusable(nvl);
(void) pthread_mutex_unlock(&sp->sch_opslock);
fmd_scheme_hash_release(fmd.d_schemes, sp);
return (rv);
}
/*
* Someday we'll retire the scheme plugins. For the
* retire/unretire operations, the topo interfaces
* are called directly.
*/
int
fmd_fmri_retire(nvlist_t *nvl)
{
topo_hdl_t *thp;
int rv, err;
if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
return (-1);
rv = topo_fmri_retire(thp, nvl, &err);
fmd_fmri_topo_rele(thp);
return (rv);
}
int
fmd_fmri_unretire(nvlist_t *nvl)
{
topo_hdl_t *thp;
int rv, err;
if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
return (-1);
rv = topo_fmri_unretire(thp, nvl, &err);
fmd_fmri_topo_rele(thp);
return (rv);
}
int
fmd_fmri_contains(nvlist_t *er, nvlist_t *ee)
{
fmd_scheme_t *sp;
char *ername, *eename;
int rv;
if (nvlist_lookup_string(er, FM_FMRI_SCHEME, &ername) != 0 ||
nvlist_lookup_string(ee, FM_FMRI_SCHEME, &eename) != 0 ||
strcmp(ername, eename) != 0)
return (fmd_set_errno(EFMD_FMRI_INVAL));
if ((sp = fmd_scheme_hash_lookup(fmd.d_schemes, ername)) == NULL)
return (-1); /* errno is set for us */
(void) pthread_mutex_lock(&sp->sch_opslock);
rv = sp->sch_ops.sop_contains(er, ee);
(void) pthread_mutex_unlock(&sp->sch_opslock);
fmd_scheme_hash_release(fmd.d_schemes, sp);
return (rv);
}
nvlist_t *
fmd_fmri_translate(nvlist_t *fmri, nvlist_t *auth)
{
fmd_scheme_t *sp;
nvlist_t *nvl;
if ((sp = nvl2scheme(fmri)) == NULL)
return (NULL); /* errno is set for us */
(void) pthread_mutex_lock(&sp->sch_opslock);
nvl = sp->sch_ops.sop_translate(fmri, auth);
(void) pthread_mutex_unlock(&sp->sch_opslock);
fmd_scheme_hash_release(fmd.d_schemes, sp);
return (nvl);
}