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
cleanup_iodevs_snapshot()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
drvinstpart2dev(char * driver,int instance,char * part,char ** devpathp,char ** adevpathp,char ** devidp)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
drvpid2port(uint_t pid,char ** target_portp)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 *
lookup_ks_name(char * ks_name,int want_devid)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 *
lookup_nfs_name(char * ks,kstat_ctl_t * kc)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 *
get_nfs_by_minor(uint_t minor)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 *
cur_hostname(uint_t minor,kstat_ctl_t * kc)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 *
cur_special(char * hostname,char * special)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