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 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2016 Nexenta Systems, Inc. 26 */ 27 28 #include <sys/stat.h> 29 #include <sys/types.h> 30 31 /* 32 * Dependent on types.h, but not including it... 33 */ 34 #include <stdio.h> 35 #include <sys/types.h> 36 #include <sys/dkio.h> 37 #include <sys/dktp/fdisk.h> 38 #include <sys/mnttab.h> 39 #include <sys/mntent.h> 40 #include <sys/sysmacros.h> 41 #include <sys/mkdev.h> 42 #include <sys/vfs.h> 43 #include <nfs/nfs.h> 44 #include <nfs/nfs_clnt.h> 45 #include <kstat.h> 46 #include <ctype.h> 47 #include <dirent.h> 48 #include <libdevinfo.h> 49 #include <limits.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <strings.h> 53 #include <unistd.h> 54 #include <errno.h> 55 #include <devid.h> 56 #include <sys/scsi/adapters/scsi_vhci.h> 57 58 #include "dsr.h" 59 #include "statcommon.h" 60 61 /* where we get kstat name translation information from */ 62 static di_node_t di_root; /* from di_init: for devid */ 63 static di_dim_t di_dim; /* from di_dim_init: for /dev names */ 64 static int scsi_vhci_fd = -1; /* from scsi_vhci: for mpxio path */ 65 66 /* disk/tape/misc info */ 67 typedef struct { 68 char *minor_name; 69 int minor_isdisk; 70 } minor_match_t; 71 static minor_match_t mm_disk = {"a", 1}; 72 static minor_match_t mm_tape = {"", 0}; 73 static minor_match_t mm_misc = {"0", 0}; 74 static minor_match_t *mma_disk_tape_misc[] = 75 {&mm_disk, &mm_tape, &mm_misc, NULL}; 76 #define DISKLIST_MOD 256 /* ^2 instance mod hash */ 77 static disk_list_t *disklist[DISKLIST_MOD]; 78 79 80 /* nfs info */ 81 extern kstat_ctl_t *kc; 82 extern mnt_t *nfs; 83 static int nfs_tried; 84 static char *get_nfs_by_minor(uint_t); 85 static char *cur_hostname(uint_t, kstat_ctl_t *); 86 static char *cur_special(char *, char *); 87 88 /* 89 * Clear the snapshot so a cache miss in lookup_ks_name() will cause a fresh 90 * snapshot in drvinstpart2dev(). 91 */ 92 void 93 cleanup_iodevs_snapshot() 94 { 95 if (di_dim) { 96 di_dim_fini(di_dim); 97 di_dim = NULL; 98 } 99 100 if (di_root) { 101 di_fini(di_root); 102 di_root = DI_NODE_NIL; 103 } 104 105 nfs_tried = 0; 106 } 107 108 /* 109 * Find information for (driver, instance) device: return zero on failure. 110 * 111 * NOTE: Failure of drvinstpart2dev works out OK for the caller if the kstat 112 * name is the same as public name: the caller will just use kstat name. 113 */ 114 static int 115 drvinstpart2dev(char *driver, int instance, char *part, 116 char **devpathp, char **adevpathp, char **devidp) 117 { 118 minor_match_t *mm, **mma = mma_disk_tape_misc; 119 char *devpath; 120 char *devid; 121 char *devicespath; 122 di_node_t node; 123 124 /* setup "no result" return values */ 125 if (devpathp != NULL) 126 *devpathp = NULL; 127 if (adevpathp != NULL) 128 *adevpathp = NULL; 129 if (devidp != NULL) 130 *devidp = NULL; 131 132 /* take <driver><instance><minor> snapshot if not established */ 133 if (di_dim == NULL) { 134 di_dim = di_dim_init(); 135 if (di_dim == NULL) 136 return (0); 137 } 138 139 if (part != NULL) { 140 devpath = di_dim_path_dev(di_dim, driver, instance, part); 141 } else { 142 /* Try to find a minor_match that works */ 143 for (mm = *mma++; mm != NULL; mm = *mma++) { 144 if ((devpath = di_dim_path_dev(di_dim, 145 driver, instance, mm->minor_name)) != NULL) 146 break; 147 } 148 } 149 if (devpath == NULL) 150 return (0); 151 152 /* 153 * At this point we have a devpath result. Return the information about 154 * the result that the caller is asking for. 155 */ 156 if (devpathp != NULL) /* devpath */ 157 *devpathp = safe_strdup(devpath); 158 159 if (adevpathp != NULL) { /* abbreviated devpath */ 160 char *a; 161 162 a = strrchr(devpath, '/'); 163 if (a == NULL) { 164 free(devpath); 165 return (0); 166 } 167 a++; 168 if (part == NULL && mm->minor_isdisk) { 169 /* 170 * For disk kstats without a partition we return the 171 * last component with trailing "s#" or "p#" stripped 172 * off (i.e. partition/slice information is removed). 173 * For example for devpath of "/dev/dsk/c0t0d0s0" the 174 * abbreviated devpath would be "c0t0d0". 175 */ 176 char *s; 177 178 if ((s = strrchr(a, 's')) == NULL && 179 (s = strrchr(a, 'p')) == NULL) { 180 free(devpath); 181 return (0); 182 } 183 /* don't include slice information in devpath */ 184 *s = '\0'; 185 } 186 *adevpathp = safe_strdup(a); 187 } 188 189 if (devidp != NULL) { /* lookup the devid */ 190 /* take snapshot if not established */ 191 if (di_root == DI_NODE_NIL) 192 di_root = di_init("/", DINFOCACHE); 193 if (di_root != NULL) { 194 /* get path to /devices devinfo node */ 195 devicespath = di_dim_path_devices(di_dim, 196 driver, instance, NULL); 197 if (devicespath) { 198 /* find the node in the snapshot */ 199 node = di_lookup_node(di_root, devicespath); 200 free(devicespath); 201 202 /* and lookup devid property on the node */ 203 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 204 DEVID_PROP_NAME, &devid) != -1) 205 *devidp = devid; 206 } 207 } 208 } 209 210 free(devpath); 211 return (1); /* success */ 212 } 213 214 /* 215 * Do <pid> to 'target-port' translation 216 */ 217 static int 218 drvpid2port(uint_t pid, char **target_portp) 219 { 220 sv_iocdata_t ioc; 221 char target_port[MAXNAMELEN]; 222 223 /* setup "no result" return values */ 224 *target_portp = NULL; 225 226 /* open scsi_vhci if not already done */ 227 if (scsi_vhci_fd == -1) { 228 scsi_vhci_fd = open("/devices/scsi_vhci:devctl", O_RDONLY); 229 if (scsi_vhci_fd == -1) 230 return (0); /* failure */ 231 } 232 233 /* 234 * Perform ioctl for <pid> -> 'target-port' translation. 235 * 236 * NOTE: it is legimite for this ioctl to fail for transports 237 * that use mpxio, but don't set a 'target-port' pathinfo property. 238 * On failure we return the the "<pid>" as the target port string. 239 */ 240 bzero(&ioc, sizeof (sv_iocdata_t)); 241 ioc.buf_elem = pid; 242 ioc.addr = target_port; 243 if (ioctl(scsi_vhci_fd, SCSI_VHCI_GET_TARGET_LONGNAME, &ioc) < 0) { 244 (void) snprintf(target_port, sizeof (target_port), "%d", pid); 245 } 246 247 *target_portp = safe_strdup(target_port); 248 return (1); /* success */ 249 } 250 251 /* 252 * Find/create a disk_list entry for the given kstat name. 253 * The basic format of a kstat name is 254 * 255 * "<driver><instance>[.<pid>.<phci-driver><instance>][,<partition>]". 256 * 257 * The <instance> is a decimal number. The ".<pid>.<phci-driver><instance>", 258 * which describes mpxio path stat information, and ",<partition>" parts are 259 * optional. The <pid> consists of the letter 't' followed by a decimal number. 260 * When available, we use the <pid> to find the 'target-port' via ioctls to 261 * the scsi_vhci driver. 262 */ 263 disk_list_t * 264 lookup_ks_name(char *ks_name, int want_devid) 265 { 266 char *pidp; /* ".<pid>... */ 267 char *part; /* ",partition... */ 268 char *initiator; /* ".<phci-driver>... */ 269 char *p; 270 int len; 271 char driver[KSTAT_STRLEN]; 272 int instance; 273 disk_list_t **dlhp; /* disklist head */ 274 disk_list_t *entry; 275 char *devpath = NULL; 276 char *adevpath = NULL; 277 char *devid = NULL; 278 int pid; 279 char *target_port = NULL; 280 char portform[MAXPATHLEN]; 281 282 /* Filter out illegal forms (like all digits) */ 283 if (ks_name == NULL || *ks_name == '\0' || 284 strspn(ks_name, "0123456789") == strlen(ks_name)) 285 goto fail; 286 287 /* parse ks_name to create new entry */ 288 pidp = strchr(ks_name, '.'); /* start of ".<pid>" */ 289 initiator = strrchr(ks_name, '.'); /* start of ".<pHCI-driver>" */ 290 if (pidp != NULL && pidp == initiator) /* can't have same start */ 291 goto fail; 292 293 part = strchr(ks_name, ','); /* start of ",<partition>" */ 294 p = strchr(ks_name, ':'); /* start of ":<partition>" */ 295 if (part != NULL && p != NULL) 296 goto fail; /* can't have both */ 297 if (p != NULL) 298 part = p; 299 if (part != NULL && pidp != NULL) 300 goto fail; /* <pid> and partition: bad */ 301 302 p = (part != NULL) ? part : pidp; 303 if (p == NULL) 304 p = &ks_name[strlen(ks_name) - 1]; /* last char */ 305 else 306 p--; /* before ',' or '.' */ 307 308 while ((p >= ks_name) && isdigit(*p)) 309 p--; /* backwards over digits */ 310 p++; /* start of instance */ 311 if ((*p == '\0') || (*p == ',') || (*p == '.') || (*p == ':')) 312 goto fail; /* no <instance> */ 313 len = p - ks_name; 314 (void) strncpy(driver, ks_name, len); 315 driver[len] = '\0'; 316 instance = atoi(p); 317 if (part != NULL) 318 part++; /* skip ',' */ 319 320 /* hash by instance and search for existing entry */ 321 dlhp = &disklist[instance & (DISKLIST_MOD - 1)]; 322 for (entry = *dlhp; entry; entry = entry->next) { 323 if (strcmp(entry->ks_name, ks_name) == 0) 324 return (entry); 325 } 326 327 /* not found, translate kstat_name components and create new entry */ 328 329 /* translate kstat_name dev information */ 330 if (drvinstpart2dev(driver, instance, part, 331 &devpath, &adevpath, want_devid ? &devid : NULL) == 0) 332 goto fail; 333 334 /* parse and translate path information */ 335 if (pidp != NULL) { 336 /* parse path information: ".t#.<phci-driver><instance>" */ 337 pidp++; /* skip '.' */ 338 initiator++; /* skip '.' */ 339 if (*pidp != 't' || !isdigit(pidp[1])) 340 goto fail; /* not ".t#" */ 341 pid = atoi(&pidp[1]); 342 343 /* translate <pid> to 'target-port' */ 344 if (drvpid2port(pid, &target_port) == 0) 345 goto fail; 346 347 /* Establish 'target-port' form. */ 348 (void) snprintf(portform, sizeof (portform), 349 "%s.t%s.%s", adevpath, target_port, initiator); 350 free(target_port); 351 free(adevpath); 352 adevpath = strdup(portform); 353 } 354 355 /* make a new entry ... */ 356 entry = safe_alloc(sizeof (disk_list_t)); 357 entry->ks_name = safe_strdup(ks_name); 358 entry->dname = devpath; 359 entry->dsk = adevpath; 360 entry->devidstr = devid; 361 362 #ifdef DEBUG 363 (void) printf("lookup_ks_name: new: %s %s\n", 364 ks_name, entry->dsk ? entry->dsk : "NULL"); 365 #endif /* DEBUG */ 366 367 /* add new entry to head of hashed list */ 368 entry->next = *dlhp; 369 *dlhp = entry; 370 return (entry); 371 372 fail: 373 free(devpath); 374 free(adevpath); 375 free(devid); 376 #ifdef DEBUG 377 (void) printf("lookup_ks_name: failed: %s\n", ks_name); 378 #endif /* DEBUG */ 379 return (NULL); 380 } 381 382 char * 383 lookup_nfs_name(char *ks, kstat_ctl_t *kc) 384 { 385 uint_t minor; 386 char *host, *path; 387 char *cp; 388 char *rstr = 0; 389 size_t len; 390 391 if (sscanf(ks, "nfs%u", &minor) == 1) { 392 retry: 393 cp = get_nfs_by_minor(minor); 394 if (cp) { 395 if (strchr(cp, ',') == NULL) { 396 rstr = safe_strdup(cp); 397 return (rstr); 398 } 399 host = cur_hostname(minor, kc); 400 if (host) { 401 if (*host) { 402 path = cur_special(host, cp); 403 if (path) { 404 len = strlen(host); 405 len += strlen(path); 406 len += 2; 407 rstr = safe_alloc(len); 408 (void) snprintf(rstr, len, 409 "%s:%s", host, path); 410 } else { 411 rstr = safe_strdup(cp); 412 } 413 } else { 414 rstr = safe_strdup(ks); 415 } 416 free(host); 417 } else { 418 rstr = safe_strdup(cp); 419 } 420 } else if (nfs_tried == 0) { 421 nfs_tried = 1; 422 do_mnttab(); 423 goto retry; 424 } 425 } 426 return (rstr); 427 } 428 429 static char * 430 get_nfs_by_minor(uint_t minor) 431 { 432 mnt_t *localnfs; 433 434 localnfs = nfs; 435 while (localnfs) { 436 if (localnfs->minor == minor) { 437 return (localnfs->device_name); 438 } 439 localnfs = localnfs->next; 440 } 441 return (0); 442 } 443 444 /* 445 * Read the cur_hostname from the mntinfo kstat 446 */ 447 static char * 448 cur_hostname(uint_t minor, kstat_ctl_t *kc) 449 { 450 kstat_t *ksp; 451 static struct mntinfo_kstat mik; 452 char *rstr; 453 454 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 455 if (ksp->ks_type != KSTAT_TYPE_RAW) 456 continue; 457 if (ksp->ks_instance != minor) 458 continue; 459 if (strcmp(ksp->ks_module, "nfs")) 460 continue; 461 if (strcmp(ksp->ks_name, "mntinfo")) 462 continue; 463 if (ksp->ks_flags & KSTAT_FLAG_INVALID) 464 return (NULL); 465 if (kstat_read(kc, ksp, &mik) == -1) 466 return (NULL); 467 rstr = safe_strdup(mik.mik_curserver); 468 return (rstr); 469 } 470 return (NULL); 471 } 472 473 /* 474 * Given the hostname of the mounted server, extract the server 475 * mount point from the mnttab string. 476 * 477 * Common forms: 478 * server1,server2,server3:/path 479 * server1:/path,server2:/path 480 * or a hybrid of the two 481 */ 482 static char * 483 cur_special(char *hostname, char *special) 484 { 485 char *cp; 486 char *path; 487 size_t hlen = strlen(hostname); 488 489 /* 490 * find hostname in string 491 */ 492 again: 493 if ((cp = strstr(special, hostname)) == NULL) 494 return (NULL); 495 496 /* 497 * hostname must be followed by ',' or ':' 498 */ 499 if (cp[hlen] != ',' && cp[hlen] != ':') { 500 special = &cp[hlen]; 501 goto again; 502 } 503 504 /* 505 * If hostname is followed by a ',' eat all characters until a ':' 506 */ 507 cp = &cp[hlen]; 508 if (*cp == ',') { 509 cp++; 510 while (*cp != ':') { 511 if (*cp == NULL) 512 return (NULL); 513 cp++; 514 } 515 } 516 path = ++cp; /* skip ':' */ 517 518 /* 519 * path is terminated by either 0, or space or ',' 520 */ 521 while (*cp) { 522 if (isspace(*cp) || *cp == ',') { 523 *cp = NULL; 524 return (path); 525 } 526 cp++; 527 } 528 return (path); 529 } 530