1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <fm/fmd_fmri.h> 30 #include <libdevinfo.h> 31 #include <alloca.h> 32 #include <string.h> 33 34 /* 35 * buf_append -- Append str to buf (if it's non-NULL). Place prepend 36 * in buf in front of str and append behind it (if they're non-NULL). 37 * Continue to update size even if we run out of space to actually 38 * stuff characters in the buffer. 39 */ 40 static void 41 buf_append(ssize_t *sz, char *buf, size_t buflen, char *str, 42 char *prepend, char *append) 43 { 44 ssize_t left; 45 46 if (str == NULL) 47 return; 48 49 if (buflen == 0 || (left = buflen - *sz) < 0) 50 left = 0; 51 52 if (buf != NULL && left != 0) 53 buf += *sz; 54 55 if (prepend == NULL && append == NULL) 56 *sz += snprintf(buf, left, "%s", str); 57 else if (append == NULL) 58 *sz += snprintf(buf, left, "%s%s", prepend, str); 59 else if (prepend == NULL) 60 *sz += snprintf(buf, left, "%s%s", str, append); 61 else 62 *sz += snprintf(buf, left, "%s%s%s", prepend, str, append); 63 } 64 65 66 ssize_t 67 fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 68 { 69 nvlist_t *anvl = NULL; 70 uint8_t version; 71 ssize_t size = 0; 72 char *devid = NULL; 73 char *devpath = NULL; 74 char *achas = NULL; 75 char *adom = NULL; 76 char *aprod = NULL; 77 char *asrvr = NULL; 78 char *ahost = NULL; 79 int more_auth = 0; 80 int err; 81 82 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 83 version > FM_DEV_SCHEME_VERSION) 84 return (fmd_fmri_set_errno(EINVAL)); 85 86 /* Get authority, if present */ 87 err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 88 if (err != 0 && err != ENOENT) 89 return (fmd_fmri_set_errno(err)); 90 91 /* Get devid, if present */ 92 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); 93 if (err != 0 && err != ENOENT) 94 return (fmd_fmri_set_errno(err)); 95 96 /* There must be a device path present */ 97 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath); 98 if (err != 0 || devpath == NULL) 99 return (fmd_fmri_set_errno(EINVAL)); 100 101 if (anvl != NULL) { 102 (void) nvlist_lookup_string(anvl, 103 FM_FMRI_AUTH_PRODUCT, &aprod); 104 (void) nvlist_lookup_string(anvl, 105 FM_FMRI_AUTH_CHASSIS, &achas); 106 (void) nvlist_lookup_string(anvl, 107 FM_FMRI_AUTH_DOMAIN, &adom); 108 (void) nvlist_lookup_string(anvl, 109 FM_FMRI_AUTH_SERVER, &asrvr); 110 (void) nvlist_lookup_string(anvl, 111 FM_FMRI_AUTH_HOST, &ahost); 112 if (aprod != NULL) 113 more_auth++; 114 if (achas != NULL) 115 more_auth++; 116 if (adom != NULL) 117 more_auth++; 118 if (asrvr != NULL) 119 more_auth++; 120 if (ahost != NULL) 121 more_auth++; 122 } 123 124 /* dev:// */ 125 buf_append(&size, buf, buflen, FM_FMRI_SCHEME_DEV, NULL, "://"); 126 127 /* authority, if any */ 128 if (aprod != NULL) 129 buf_append(&size, buf, buflen, aprod, FM_FMRI_AUTH_PRODUCT "=", 130 --more_auth > 0 ? "," : NULL); 131 if (achas != NULL) 132 buf_append(&size, buf, buflen, achas, FM_FMRI_AUTH_CHASSIS "=", 133 --more_auth > 0 ? "," : NULL); 134 if (adom != NULL) 135 buf_append(&size, buf, buflen, adom, FM_FMRI_AUTH_DOMAIN "=", 136 --more_auth > 0 ? "," : NULL); 137 if (asrvr != NULL) 138 buf_append(&size, buf, buflen, asrvr, FM_FMRI_AUTH_SERVER "=", 139 --more_auth > 0 ? "," : NULL); 140 if (ahost != NULL) 141 buf_append(&size, buf, buflen, ahost, FM_FMRI_AUTH_HOST "=", 142 NULL); 143 144 /* device-id part */ 145 buf_append(&size, buf, buflen, devid, "/:" FM_FMRI_DEV_ID "=", NULL); 146 147 /* device-path part */ 148 buf_append(&size, buf, buflen, devpath, "/", NULL); 149 150 return (size); 151 } 152 153 /* 154 * callback routine for di_walk_minor() 155 */ 156 struct walkinfo { 157 int matched; 158 const char *path; 159 int len; 160 }; 161 162 static int 163 dev_match(di_node_t node, void *arg) 164 { 165 struct walkinfo *wip = (struct walkinfo *)arg; 166 char *path = di_devfs_path(node); 167 168 if (path != NULL && strncmp(path, wip->path, wip->len) == 0) { 169 /* 170 * found the match we were looking for, set matched 171 * flag and terminate the walk. 172 */ 173 wip->matched = 1; 174 di_devfs_path_free(path); 175 return (DI_WALK_TERMINATE); 176 } 177 178 if (path != NULL) 179 di_devfs_path_free(path); 180 return (DI_WALK_CONTINUE); 181 } 182 183 /* 184 * For now we only check for the presence of the device in the device 185 * tree. This is somewhat unsophisticated, because a device may have 186 * been inserted into the same slot as the previous ASRU and we don't 187 * know how to tell them apart yet. 188 */ 189 int 190 fmd_fmri_present(nvlist_t *nvl) 191 { 192 di_node_t parent; 193 uint8_t version; 194 char *devpath = NULL; 195 char *parentpath; 196 char *cp; 197 struct walkinfo walkinfo; 198 199 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 200 version > FM_DEV_SCHEME_VERSION || 201 nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath) != 0) 202 return (fmd_fmri_set_errno(EINVAL)); 203 204 if (devpath == NULL || (walkinfo.len = strlen(devpath)) == 0) 205 return (fmd_fmri_set_errno(EINVAL)); 206 207 /* strip off last component of path */ 208 parentpath = alloca(walkinfo.len + 1); 209 (void) strcpy(parentpath, devpath); 210 if ((cp = strrchr(parentpath, '/')) == NULL) 211 parentpath = "/"; 212 else 213 *cp = '\0'; 214 215 /* if the result is an empty path, start walk at "/" */ 216 if (*parentpath == '\0') 217 parentpath = "/"; 218 219 if ((parent = di_init(parentpath, DINFOSUBTREE)) == DI_NODE_NIL) 220 return (errno == ENXIO ? 0 : -1); 221 222 walkinfo.matched = 0; 223 walkinfo.path = devpath; 224 (void) di_walk_node(parent, 225 DI_WALK_SIBFIRST, (void *)&walkinfo, dev_match); 226 di_fini(parent); 227 228 return (walkinfo.matched); 229 } 230 231 /* 232 * We presently don't have a good indication of the usability of an 233 * ASRU in the dev scheme, so we'll assume its usable. 234 */ 235 int 236 fmd_fmri_unusable(nvlist_t *nvl) 237 { 238 uint8_t version; 239 240 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 241 version > FM_DEV_SCHEME_VERSION) 242 return (fmd_fmri_set_errno(EINVAL)); 243 244 return (0); 245 } 246