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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <stropts.h>
35 #include <strings.h>
36 #include <sys/param.h>
37 #include <libdevinfo.h>
38 #include <locale.h>
39 #include <libintl.h>
40 #include <devid.h>
41 #include <sys/libdevid.h>
42 #include <sys/modctl.h> /* for MAXMODCONFNAME */
43 #include <sys/scsi/adapters/scsi_vhci.h>
44
45 /*
46 * SAVE_DIR is the directory in which system files are saved.
47 * SAVE_DIR must be under the root filesystem, as this program is
48 * typically run before any other filesystems are mounted.
49 */
50 #define SAVE_DIR "/etc/mpxio"
51 #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl"
52
53 /* nvlist property names, these are ALL string types */
54 #define NVL_DEVID "nvl-devid"
55 #define NVL_PATH "nvl-path"
56 #define NVL_PHYSPATH "nvl-physpath"
57 #define NVL_MPXPATH "nvl-mpxiopath"
58 #define NVL_MPXEN "nvl-mpxioenabled"
59
60 #define MPX_LIST 0x01
61 #define MPX_MAP 0x02
62 #define MPX_CAPABLE_CTRL 0x04
63 #define MPX_DEV_PATH 0x06
64 #define MPX_INIT 0x08
65 #define MPX_PHYSICAL 0x10
66 #define MPX_BOOTPATH 0x20
67 #define MPX_UPDATEVFSTAB 0x40
68 #define MPX_GETPATH 0x60
69 #define MPX_USAGE 0x80
70 #define MSG_INFO 0x01
71 #define MSG_ERROR 0x02
72 #define MSG_PANIC 0x04
73
74 #define BOOT_PATH 0x02
75 #define BOOT 0x01
76 #define NONBOOT 0x00
77
78 #define DISPLAY_ONE_PATH 0x00
79 #define DISPLAY_ALL_PATH 0x01
80
81 static di_node_t devinfo_root = DI_NODE_NIL;
82 static char *ondiskname = "/etc/mpxio/devid_path.cache";
83
84 /*
85 * We use devid-keyed nvlists to keep track of the guid, traditional and
86 * MPxIO-enabled /dev/rdsk paths. Each of these nvlists is eventually
87 * added to our global nvlist and our on-disk nvlist.
88 */
89 static nvlist_t *mapnvl;
90 static int mpxenabled = 0;
91 static int limctrl = -1;
92 static int mpxprop = 0;
93 static int guid = 0;
94 static char *drvlimit;
95 static int globarg = 0;
96 static int debugflag = 0;
97 static char *devicep;
98 static int readonlyroot = 0;
99 static int cap_N_option = 0;
100
101 static void print_mpx_capable(di_node_t curnode);
102 static int popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl,
103 char *strdevid);
104 static int mpxio_nvl_boilerplate(di_node_t curnode);
105 static int validate_devnvl();
106 static void report_map(char *argdev, int physpath);
107 static void list_devs(int listguids, int ctrl);
108 static void logmsg(int level, const char *msg, ...);
109 static char *find_link(di_node_t cnode);
110 static void usage();
111 static void parse_args(int argc, char *argv[]);
112 static void get_devid(di_node_t node, ddi_devid_t *thisdevid);
113 static int print_bootpath();
114 static void vhci_to_phci(char *devpath, char *slice, int d_flag);
115 static int update_vfstab();
116 static void report_dev_node_name(char *strdevfspath);
117 static void print_node_name(char *drv_name, char *strdevfspath);
118 int
main(int argc,char ** argv)119 main(int argc, char **argv)
120 {
121 struct stat cachestat;
122 int mapfd = 0;
123 int rv = 0;
124 char *ondiskbuf;
125 size_t newsz = 0;
126
127 parse_args(argc, argv);
128 errno = 0;
129 devinfo_root = di_init("/", DINFOCPYALL|DINFOFORCE);
130 logmsg(MSG_INFO, "errno = %d after "
131 "di_init(/,DINFOCPYALL|DINFOFORCE)\n", errno);
132 if (devinfo_root == NULL) {
133 logmsg(MSG_ERROR,
134 gettext("Unable to take device tree snapshot "
135 "(%s: %d)\n"), strerror(errno), errno);
136 return (-1);
137 }
138 logmsg(MSG_INFO, "opened root di_node\n");
139
140 if (globarg == MPX_CAPABLE_CTRL) {
141 /* we just want to find MPxIO-capable controllers and exit */
142 if (drvlimit != NULL) {
143 print_mpx_capable(di_drv_first_node(drvlimit,
144 devinfo_root));
145 } else {
146 print_mpx_capable(di_drv_first_node("fp",
147 devinfo_root));
148 print_mpx_capable(di_drv_first_node("mpt",
149 devinfo_root));
150 print_mpx_capable(di_drv_first_node("mpt_sas",
151 devinfo_root));
152 print_mpx_capable(di_drv_first_node("pmcs",
153 devinfo_root));
154 }
155 di_fini(devinfo_root);
156 return (0);
157 }
158
159 mapfd = open(ondiskname, O_RDWR|O_CREAT|O_SYNC, S_IRUSR | S_IWUSR);
160 if (mapfd < 0) {
161 /* we could be in single-user, so try for RO */
162 if ((mapfd = open(ondiskname, O_RDONLY)) < 0) {
163 logmsg(MSG_ERROR,
164 gettext("Unable to open or create %s:%s\n"),
165 ondiskname, strerror(errno));
166 return (errno);
167 }
168 readonlyroot = 1;
169 }
170
171 if (stat(ondiskname, &cachestat) != 0) {
172 logmsg(MSG_ERROR,
173 gettext("Unable to stat() %s: %s\n"),
174 ondiskname, strerror(errno));
175 return (errno);
176 }
177 ondiskbuf = calloc(1, cachestat.st_size);
178 if (ondiskbuf == NULL) {
179 logmsg(MSG_ERROR,
180 gettext("Unable to allocate memory for the devid "
181 "cache file: %s\n"), strerror(errno));
182 return (errno);
183 }
184 rv = read(mapfd, ondiskbuf, cachestat.st_size);
185 if (rv != cachestat.st_size) {
186 logmsg(MSG_ERROR,
187 gettext("Unable to read all of devid cache file (got %d "
188 "from expected %d bytes): %s\n"),
189 rv, cachestat.st_size, strerror(errno));
190 return (errno);
191 }
192 errno = 0;
193 rv = nvlist_unpack(ondiskbuf, cachestat.st_size, &mapnvl, 0);
194 if (rv) {
195 logmsg(MSG_INFO,
196 "Unable to unpack devid cache file %s: %s (%d)\n",
197 ondiskname, strerror(rv), rv);
198 if (nvlist_alloc(&mapnvl, NV_UNIQUE_NAME, 0) != 0) {
199 logmsg(MSG_ERROR,
200 gettext("Unable to allocate root property"
201 "list\n"));
202 return (errno);
203 }
204 }
205 free(ondiskbuf);
206
207 if (validate_devnvl() < 0) {
208 logmsg(MSG_ERROR,
209 gettext("unable to validate kernel with on-disk devid "
210 "cache file\n"));
211 return (errno);
212 }
213
214 /*
215 * If we're in single-user mode or maintenance mode, we won't
216 * necessarily have a writable root device (ZFSroot; ufs root is
217 * different in that we _do_ have a writable root device.
218 * This causes problems for the devlink calls (see
219 * $SRC/lib/libdevinfo/devinfo_devlink.c) and we do not try to
220 * write out the devnvl if root is readonly.
221 */
222 if (!readonlyroot) {
223 rv = nvlist_size(mapnvl, &newsz, NV_ENCODE_NATIVE);
224 if (rv) {
225 logmsg(MSG_ERROR,
226 gettext("Unable to determine size of packed "
227 "on-disk devid cache file %s: %s (%d).\n"),
228 ondiskname, strerror(rv), rv);
229 logmsg(MSG_ERROR, gettext("Terminating\n"));
230 nvlist_free(mapnvl);
231 (void) close(mapfd);
232 return (rv);
233 }
234
235 if ((ondiskbuf = calloc(1, newsz)) == NULL) {
236 logmsg(MSG_ERROR,
237 "Unable to allocate space for writing out new "
238 "on-disk devid cache file: %s\n", strerror(errno));
239 (void) close(mapfd);
240 nvlist_free(mapnvl);
241 return (errno);
242 }
243
244 rv = nvlist_pack(mapnvl, &ondiskbuf, &newsz,
245 NV_ENCODE_NATIVE, 0);
246 if (rv) {
247 logmsg(MSG_ERROR,
248 gettext("Unable to pack on-disk devid cache "
249 "file: %s (%d)\n"), strerror(rv), rv);
250 (void) close(mapfd);
251 free(ondiskbuf);
252 nvlist_free(mapnvl);
253 return (rv);
254 }
255
256 rv = lseek(mapfd, 0, 0);
257 if (rv == -1) {
258 logmsg(MSG_ERROR,
259 gettext("Unable to seek to start of devid cache "
260 "file: %s (%d)\n"), strerror(errno), errno);
261 (void) close(mapfd);
262 free(ondiskbuf);
263 nvlist_free(mapnvl);
264 return (-1);
265 }
266
267 if (write(mapfd, ondiskbuf, newsz) != newsz) {
268 logmsg(MSG_ERROR,
269 gettext("Unable to completely write out "
270 "on-disk devid cache file: %s\n"), strerror(errno));
271 (void) close(mapfd);
272 nvlist_free(mapnvl);
273 free(ondiskbuf);
274 return (errno);
275 }
276 } /* !readonlyroot */
277
278 /* Now we can process the command line args */
279 if (globarg == MPX_PHYSICAL) {
280 report_map(devicep, BOOT);
281 } else if (globarg == MPX_BOOTPATH) {
282 rv = print_bootpath();
283 di_fini(devinfo_root);
284 return (rv);
285 } else if (globarg == MPX_UPDATEVFSTAB) {
286 rv = update_vfstab();
287 di_fini(devinfo_root);
288 return (rv);
289 } else if (globarg == MPX_GETPATH) {
290 report_dev_node_name(devicep);
291 } else if (globarg == MPX_DEV_PATH) {
292 report_map(devicep, BOOT_PATH);
293 } else if (globarg != MPX_INIT) {
294 if (globarg & MPX_LIST)
295 list_devs(guid, limctrl);
296
297 if (globarg == MPX_MAP)
298 report_map(devicep, NONBOOT);
299 } else {
300 logmsg(MSG_INFO, "\nprivate devid cache file initialised\n");
301 }
302
303 nvlist_free(mapnvl);
304 di_fini(devinfo_root);
305 return (0);
306 }
307
308 static void
usage()309 usage()
310 {
311 (void) fprintf(stderr,
312 gettext("usage: stmsboot_util -b | -m devname | "
313 "-l <ctrl> | -L | [-g] | -n | -N | -i | -p devname\n"));
314 (void) fprintf(stderr, "\n\n");
315 (void) fprintf(stderr, gettext("\t-h\tprint this usage message\n"));
316 (void) fprintf(stderr, gettext("\t-b\tretrieve the system's bootpath "
317 "setting\n"));
318 (void) fprintf(stderr, gettext("\t-m devname\n"));
319 (void) fprintf(stderr, gettext("\t\tReports the current mapping for "
320 "devname\n"));
321 (void) fprintf(stderr, gettext("\t-g\tprint the GUID for MPxIO-capable "
322 "devices. This\n"));
323 (void) fprintf(stderr, gettext("\t\toption is only valid with the -L "
324 "or -l options\n"));
325 (void) fprintf(stderr, gettext("\t-L | -l <ctrl>\n"));
326 (void) fprintf(stderr, gettext("\t\tList the 'native' to 'MPxIO' "
327 "device mappings. If <ctrl>\n"));
328 (void) fprintf(stderr, gettext("\t\tis specified, only print mappings "
329 "for those devices\n"));
330 (void) fprintf(stderr, gettext("\t\tattached via the specified "
331 "controller.\n"));
332 (void) fprintf(stderr, gettext("\t-i\tinitialise the private devid "
333 "cache file and exit\n"));
334 (void) fprintf(stderr, gettext("\t\tThis option excludes all "
335 "others.\n"));
336 (void) fprintf(stderr, gettext("\t-n\tprint the devfs paths for "
337 "multipath-capable\n"));
338 (void) fprintf(stderr, gettext("\t\tcontroller ports.\n"));
339 (void) fprintf(stderr, gettext("\t-N\tprint the device aliases of "
340 "multipath-capable\n"));
341 (void) fprintf(stderr, gettext("\t\tcontroller ports.\n"));
342 (void) fprintf(stderr, gettext("\t-p\tdevname\n"));
343 (void) fprintf(stderr, gettext("\t\tThis option provides the physical "
344 "devfs path for\n"));
345 (void) fprintf(stderr, gettext("\t\ta specific device (devname). Used "
346 "to set the bootpath\n"));
347 (void) fprintf(stderr, gettext("\t\tvariable on x86/x64 systems\n"));
348 (void) fprintf(stderr, gettext("\t-u\ttranslates device mappings in "
349 "/etc/vfstab as \n"));
350 (void) fprintf(stderr, gettext("\t\trequired. The output is written "
351 "to /etc/mpxio/vfstab.new\n\n"));
352 exit(2);
353 }
354
355 static void
parse_args(int argc,char * argv[])356 parse_args(int argc, char *argv[])
357 {
358 char opt;
359
360 if (argc == 1)
361 usage();
362
363 /*
364 * -b prints the bootpath property
365 * -d turns on debug mode for this utility (copious output!)
366 * -D drvname
367 * if supplied, indicates that we're going to operate on
368 * devices attached to this driver.
369 * -g if (-l or -L), prints guids for devices rather than paths
370 * -h prints the usage() help text.
371 * -i initialises the cache file and exits.
372 * -l controller
373 * list non-STMS to STMS device name mappings for the specific
374 * controller, when MPxIO is enabled only.
375 * -L list non-STMS to STMS device name mappings for all controllers
376 * when MPxIO is enabled only.
377 * -m devname
378 * prints the device path (/dev/rdsk) that devname maps to
379 * in the currently-running system.
380 * -n
381 * if supplied, returns name of STMS-capable controller nodes.
382 * If the -D drvname option is specified as well, we only report
383 * nodes attached with drvname.
384 * -N
385 * same as the -n option, except that we only print the
386 * node-name (dev_info :: devi_node_name). Multiple instances
387 * through the libdevinfo snapshot are uniqified and separated
388 * by the "|" character for direct use by egrep(1).
389 * -p devname
390 * prints the physical devfs path for devname. Only used to
391 * determine the bootpath.
392 * -u
393 * remaps devices in /etc/vfstab, saving the newly generated
394 * file to /etc/mpxio/vfstab.new. If we have any remapped
395 * devices, exit with status 0, otherwise -1 for error.
396 */
397 while ((opt = getopt(argc, argv, "bdD:ghil:Lm:nNo:p:q:u")) != EOF) {
398 switch (opt) {
399 case 'b':
400 globarg = MPX_BOOTPATH;
401 break;
402 case 'd':
403 debugflag = 1;
404 break;
405 case 'D':
406 if ((drvlimit = calloc(1, MAXMODCONFNAME)) == NULL) {
407 logmsg(MSG_ERROR,
408 gettext("Unable to allocate memory for a "
409 "driver name: %s\n"), strerror(errno));
410 exit(errno);
411 }
412 bcopy(optarg, drvlimit, strlen(optarg));
413 /* update this if adding support for a new driver */
414 if ((strncmp(drvlimit, "fp", 2) == NULL) &&
415 (strncmp(drvlimit, "mpt", 3) == NULL) &&
416 (strncmp(drvlimit, "mpt_sas", 7) == NULL) &&
417 (strncmp(drvlimit, "pmcs", 4) == NULL)) {
418 logmsg(MSG_ERROR,
419 gettext("invalid parent driver (%s) "
420 "specified"), drvlimit);
421 usage();
422 }
423 break;
424 case 'h':
425 /* Just drop out and print the usage() output */
426 globarg = MPX_USAGE;
427 break;
428 case 'i':
429 globarg = MPX_INIT;
430 break;
431 case 'l':
432 globarg |= MPX_LIST;
433 limctrl = (int)atol(optarg);
434 if (limctrl < 0) {
435 logmsg(MSG_INFO,
436 gettext("invalid controller number "
437 "(%d), checking all controllers\n"),
438 limctrl);
439 }
440 break;
441 case 'L':
442 globarg |= MPX_LIST;
443 break;
444 case 'g':
445 guid = 1;
446 break;
447 case 'm':
448 globarg = MPX_MAP;
449 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
450 logmsg(MSG_ERROR,
451 gettext("Unable to allocate space for a "
452 "device name\n"));
453 exit(errno);
454 }
455 devicep = strdup(optarg);
456 break;
457 case 'N':
458 cap_N_option = 1;
459 globarg = MPX_CAPABLE_CTRL;
460 break;
461 case 'n':
462 globarg = MPX_CAPABLE_CTRL;
463 break;
464 case 'o':
465 globarg = MPX_GETPATH;
466 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
467 logmsg(MSG_ERROR,
468 gettext("Unable to allocate space for a "
469 "device name\n"));
470 exit(errno);
471 }
472 devicep = strdup(optarg);
473 break;
474 case 'p':
475 globarg = MPX_PHYSICAL;
476 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
477 logmsg(MSG_ERROR,
478 gettext("Unable to allocate space for a "
479 "device name\n"));
480 exit(errno);
481 }
482 devicep = strdup(optarg);
483 break;
484 case 'q':
485 globarg = MPX_DEV_PATH;
486 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
487 logmsg(MSG_ERROR,
488 gettext("Unable to allocate space for a "
489 "device name\n"));
490 exit(errno);
491 }
492 devicep = strdup(optarg);
493 break;
494 case 'u':
495 globarg = MPX_UPDATEVFSTAB;
496 break;
497 default:
498 logmsg(MSG_ERROR,
499 gettext("Invalid command line option (%c)\n"),
500 opt);
501 usage();
502 }
503 }
504
505 if ((globarg >= MPX_USAGE) || (guid && (globarg != MPX_LIST)))
506 usage();
507
508 if ((drvlimit != NULL) &&
509 ((globarg != MPX_LIST) &&
510 (globarg != MPX_CAPABLE_CTRL)))
511 usage();
512 }
513
514 static void
logmsg(int level,const char * msg,...)515 logmsg(int level, const char *msg, ...)
516 {
517 va_list ap;
518
519 if ((level >= MSG_ERROR) ||
520 ((debugflag > 0) && (level >= MSG_INFO))) {
521 (void) fprintf(stdout, "stmsboot: ");
522 va_start(ap, msg);
523 (void) vfprintf(stdout, msg, ap);
524 va_end(ap);
525 }
526 }
527
528 /*
529 * It's up to the caller to do any sorting or pretty-printing of the device
530 * mappings we report. Since we're storing the device links as just the cXtYdZ
531 * part, we'll add /dev/rdsk/ back on when we print the listing so we maintain
532 * compatibility with previous versions of this tool. There's a little bit
533 * of footwork involved to make sure that we show all the paths to a device
534 * rather than just the first one we stashed away.
535 */
536 static void
list_devs(int listguids,int ctrl)537 list_devs(int listguids, int ctrl)
538 {
539 nvlist_t *thisdevnvl;
540 nvpair_t *pair;
541 char *diskpath, *livepath, *key, *querydev;
542 char *matchctrl = NULL;
543 char checkctrl[MAXPATHLEN];
544 int rv;
545
546 if (!mpxenabled) {
547 if (mpxprop) {
548 logmsg(MSG_ERROR, gettext("MPXIO disabled\n"));
549 } else {
550 logmsg(MSG_ERROR, gettext("No STMS devices have "
551 "been found\n"));
552 }
553 return;
554 }
555
556 if (listguids) {
557 (void) printf(gettext("non-STMS device name\t\t\tGUID\n"
558 "------------------------------------------"
559 "------------------------\n"));
560 } else {
561 (void) printf(gettext("non-STMS device name\t\t\t"
562 "STMS device name\n"
563 "------------------------------------------"
564 "------------------------\n"));
565 }
566
567 bzero(checkctrl, MAXPATHLEN);
568 pair = NULL;
569 while ((pair = nvlist_next_nvpair(mapnvl, pair))
570 != NULL) {
571 boolean_t livescsivhcip = B_FALSE;
572
573 if ((((rv = nvpair_value_string(pair, &querydev)) < 0) ||
574 ((key = nvpair_name(pair)) == NULL)) ||
575 ((strstr(key, "/pci") != NULL) ||
576 (strstr(key, "/sbus") != NULL) ||
577 (strstr(key, "/scsi_vhci") != NULL) ||
578 (strncmp(key, "id1", 3) == 0))) {
579 logmsg(MSG_INFO,
580 "list_devs: rv = %d; (%s) is not a devlink, "
581 "continuing.\n", rv,
582 (key != NULL) ? key : "null");
583 querydev = NULL;
584 continue;
585 }
586
587 (void) nvlist_lookup_nvlist(mapnvl, querydev, &thisdevnvl);
588 (void) nvlist_lookup_boolean_value(thisdevnvl, NVL_MPXEN,
589 &livescsivhcip);
590 (void) nvlist_lookup_string(thisdevnvl, NVL_MPXPATH,
591 &livepath);
592
593 if ((!livescsivhcip) ||
594 (livescsivhcip &&
595 (strncmp(key, livepath, strlen(key)) == 0)))
596 continue;
597
598 (void) nvlist_lookup_string(thisdevnvl, NVL_PATH,
599 &diskpath);
600
601 logmsg(MSG_INFO,
602 "list_devs: %s :: %s ::%s :: MPXEN (%s)\n",
603 key, diskpath, livepath,
604 ((livescsivhcip) ? "TRUE" : "FALSE"));
605
606 if (ctrl > -1) {
607 (void) sprintf(checkctrl, "c%dt", ctrl);
608 matchctrl = strstr(key, checkctrl);
609 if (matchctrl == NULL)
610 continue;
611 }
612 if (listguids != 0) {
613 char *tempguid;
614 ddi_devid_t curdevid;
615 int rv;
616
617 rv = devid_str_decode(querydev, &curdevid, NULL);
618 if (rv == -1) {
619 logmsg(MSG_INFO, "Unable to decode devid %s\n",
620 key);
621 continue;
622 }
623 tempguid = devid_to_guid(curdevid);
624 if (tempguid != NULL)
625 (void) printf("/dev/rdsk/%s\t%s\n",
626 diskpath, tempguid);
627
628 devid_free_guid(tempguid);
629 devid_free(curdevid);
630 continue;
631 }
632
633 (void) printf("/dev/rdsk/%s\t/dev/rdsk/%s\n",
634 (strstr(key, diskpath) == NULL) ? key : diskpath,
635 livepath);
636 }
637 }
638
639 /*
640 * We get passed a device name which we search the mapnvl for. If we find
641 * it, we print the mapping as it is found. It is up to the caller of this
642 * utility to do any pretty-printing of the results. If a device listed on
643 * the command line does not exist in the mapnvl, then we print NOT_MAPPED.
644 * Otherwise we print the command-line device name as it maps to what is
645 * stashed in the mapnvl - even if that's a "no change" device mapping.
646 *
647 * Example output (-p maps to physpath=BOOT)
648 * # /lib/mpxio/stmsboot_util -p \
649 * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a
650 * /scsi_vhci/disk@g500000e011e17720:a
651 *
652 * Or the reverse:
653 * # /lib/mpxio/stmsboot_util -p /scsi_vhci/disk@g500000e011e17720:a
654 * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a
655 *
656 * For the -m option, used when we're trying to find the root device mapping:
657 *
658 * # /lib/mpxio/stmsboot_util -m /dev/dsk/c2t0d0s2
659 * /dev/dsk/c3t500000E011637CF0d0s2
660 */
661 static void
report_map(char * argdev,int physpath)662 report_map(char *argdev, int physpath)
663 {
664 nvlist_t *thisdev;
665 int rv = 0;
666 char *thisdevid;
667 char *mpxpath = NULL;
668 char *prefixt = NULL;
669 char *prefixp = NULL;
670 char *stripdev = NULL;
671 char *slice = NULL;
672 boolean_t mpxenp;
673 uint_t slicelen = 0;
674
675 mpxenp = B_FALSE;
676
677 if ((prefixt = calloc(1, strlen(argdev) + 1)) == NULL) {
678 logmsg(MSG_INFO, "Unable to allocate memory\n");
679 (void) printf("NOT_MAPPED\n");
680 return;
681 }
682
683 (void) strlcpy(prefixt, argdev, strlen(argdev) + 1);
684
685 slice = strrchr(argdev, (physpath == NONBOOT) ? 's' : ':');
686 if (slice != NULL) {
687 slicelen = strlen(slice);
688 if (slicelen > 3)
689 /* invalid size - max is 3 chars */
690 slicelen = 0;
691 }
692
693 if ((stripdev = calloc(1, strlen(prefixt) + 1)) == NULL) {
694 logmsg(MSG_INFO, "Unable to allocate memory\n");
695 (void) printf("NOT_MAPPED\n");
696 free(prefixt);
697 return;
698 }
699
700 if ((strstr(prefixt, "/scsi_vhci") == NULL) &&
701 (strstr(prefixt, "/pci") == NULL) &&
702 (strstr(prefixt, "/sbus") == NULL)) {
703 prefixp = strrchr(prefixt, '/');
704 (void) strlcpy(stripdev,
705 (prefixp == NULL) ? prefixt : prefixp + 1,
706 (prefixp == NULL) ?
707 strlen(prefixt) + 1: strlen(prefixp) + 1);
708 if (prefixp != NULL)
709 prefixt[strlen(argdev) - strlen(prefixp) + 1] = '\0';
710 } else {
711 if ((physpath != BOOT) &&
712 (physpath != BOOT_PATH)) {
713 logmsg(MSG_INFO, "Invalid device path provided\n");
714 (void) printf("NOT_MAPPED\n");
715 free(stripdev);
716 free(prefixt);
717 return;
718 }
719 (void) strlcpy(stripdev, argdev, strlen(argdev) + 1);
720 }
721
722 logmsg(MSG_INFO,
723 "stripdev (%s), prefixt(%s), prefixp(%s), slice(%s)\n",
724 (stripdev == NULL) ? "null" : stripdev,
725 (prefixt == NULL) ? "null" : prefixt,
726 (prefixp == NULL) ? "null" : prefixp,
727 (slice == NULL) ? "null" : slice);
728
729 if (slicelen > 0)
730 stripdev[strlen(stripdev) - slicelen] = '\0';
731
732 /* search for the shortened version */
733 rv = nvlist_lookup_string(mapnvl, stripdev, &thisdevid);
734 if (rv) {
735 if ((physpath != BOOT) &&
736 (physpath != BOOT_PATH)) {
737 logmsg(MSG_INFO,
738 "searched mapnvl for '%s', got %s (%d)\n",
739 stripdev, strerror(rv), rv);
740 (void) printf("NOT_MAPPED\n");
741 free(stripdev);
742 free(prefixt);
743 return;
744 }
745 }
746
747 logmsg(MSG_INFO, "device %s has devid %s\n", stripdev, thisdevid);
748
749 if (nvlist_lookup_nvlist(mapnvl, thisdevid, &thisdev) != 0) {
750 logmsg(MSG_INFO, "device (%s) in mapnvl but "
751 "not mapped!\n", thisdevid);
752 (void) printf("NOT_MAPPED\n");
753 free(stripdev);
754 free(prefixt);
755 return;
756 }
757
758 /* quick exit */
759 if (!mpxenabled && (strstr(argdev, "/pci") != NULL ||
760 strstr(argdev, "/sbus") != NULL)) {
761 (void) printf("%s\n", argdev);
762 free(stripdev);
763 free(prefixt);
764 return;
765 }
766
767 (void) nvlist_lookup_boolean_value(thisdev, NVL_MPXEN, &mpxenp);
768
769 if (physpath == BOOT) {
770 (void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath);
771 if ((strstr(argdev, "/scsi_vhci") != NULL) &&
772 (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) {
773 /* Need to translate vhci to phci */
774 vhci_to_phci(stripdev, slice, DISPLAY_ONE_PATH);
775 } else {
776 (void) printf("%s%s\n", mpxpath,
777 ((slicelen > 0) && slice != NULL) ? slice : "");
778 }
779 } else if (physpath == BOOT_PATH) {
780 (void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath);
781 if ((strstr(argdev, "/scsi_vhci") != NULL) &&
782 (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) {
783 /* Need to translate vhci to phci */
784 vhci_to_phci(stripdev, slice, DISPLAY_ALL_PATH);
785 } else {
786 (void) printf("%s%s\n", mpxpath,
787 ((slicelen > 0) && slice != NULL) ? slice : "");
788 }
789 } else {
790 (void) nvlist_lookup_string(thisdev,
791 ((readonlyroot) ? NVL_PHYSPATH :
792 ((mpxenp == B_TRUE) ? NVL_MPXPATH : NVL_PATH)),
793 &mpxpath);
794 logmsg(MSG_INFO, "mpxpath = %s\n",
795 (mpxpath == NULL) ? "null" : mpxpath);
796 if (readonlyroot ||
797 (strstr(mpxpath, "/scsi_vhci") != NULL) ||
798 (strstr(mpxpath, "/pci") != NULL) ||
799 (strstr(mpxpath, "/sbus") != NULL)) {
800 /*
801 * If we see a physical path here it means that
802 * devlinks aren't fully initialised yet, so we
803 * are still in maintenance/single-user mode.
804 */
805 (void) printf("/devices%s:%c\n", mpxpath,
806 slice[1] + '1');
807 } else {
808 (void) printf("%s%s%s\n",
809 (prefixt[0] == '/') ? prefixt : "",
810 mpxpath,
811 ((slicelen > 0) && slice != NULL) ? slice : "");
812 }
813 }
814 free(prefixt);
815 free(stripdev);
816 }
817
818 /*
819 * Validate the in-kernel and on-disk forms of our devid cache,
820 * returns -1 for unfixable error and 0 for success.
821 */
822 static int
validate_devnvl()823 validate_devnvl()
824 {
825 di_node_t curnode;
826 int rv1 = -1;
827 int rv2 = -1;
828
829 /*
830 * Method: we walk through the kernel's concept of the device tree
831 * looking for "ssd" then "sd" nodes.
832 * We check to see whether the device's devid is already in our nvlist
833 * (on disk) nvlist cache file. If it is, we check that it's components
834 * match what we've got already and fill any missing fields.
835 * If the devid isn't in our on-disk nvlist already then we add it
836 * and populate the property nvpairs.
837 *
838 * At the end of this function we should have this program's concept
839 * of the devid-keyed nvlist matching what is in the ondisk form which
840 * is ready to be written out.
841 * If we can't do this, then we return -1.
842 */
843 curnode = di_drv_first_node("ssd", devinfo_root);
844 if (curnode != DI_NODE_NIL)
845 rv1 = mpxio_nvl_boilerplate(curnode);
846
847 curnode = di_drv_first_node("sd", devinfo_root);
848 if (curnode != DI_NODE_NIL)
849 rv2 = mpxio_nvl_boilerplate(curnode);
850
851 if (rv1 + rv2 == -2)
852 return (-1);
853
854 return (0);
855 }
856
857 /*
858 * According to devfs path name, it will print device node name.
859 */
860 static void
print_node_name(char * drv_name,char * strdevfspath)861 print_node_name(char *drv_name, char *strdevfspath)
862 {
863 di_node_t curnode;
864 char *devfspath = NULL;
865 char *node_name = NULL;
866
867 curnode = di_drv_first_node(drv_name, devinfo_root);
868 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
869 devfspath = di_devfs_path(curnode);
870 logmsg(MSG_INFO, "find: devfspath %s\n", devfspath);
871
872 if (devfspath == NULL)
873 continue;
874
875 if ((strlen(strdevfspath) == strlen(devfspath)) &&
876 (strncmp(strdevfspath, devfspath,
877 strlen(devfspath)) == 0)) {
878 node_name = find_link(curnode);
879 if (node_name == NULL) {
880 (void) printf("NOT MAPPED\n");
881 } else {
882 (void) printf("%s\n", node_name);
883 }
884 return;
885 }
886 }
887 }
888
889 /*
890 * report device node name, search "ssd" and "sd" nodes,
891 * print the device node name which device path is same as
892 * parameter.
893 */
894 static void
report_dev_node_name(char * strdevfspath)895 report_dev_node_name(char *strdevfspath)
896 {
897 logmsg(MSG_INFO, "strdevfspath: %s\n", strdevfspath);
898 print_node_name("ssd", strdevfspath);
899 print_node_name("sd", strdevfspath);
900 }
901
902 static int
mpxio_nvl_boilerplate(di_node_t curnode)903 mpxio_nvl_boilerplate(di_node_t curnode)
904 {
905 int rv;
906 char *strdevid;
907 ddi_devid_t curdevid;
908 nvlist_t *newnvl;
909
910 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
911 errno = 0;
912
913 curdevid = NULL;
914 get_devid(curnode, &curdevid);
915 if (curdevid == NULL)
916 /*
917 * There's no devid registered for this device
918 * so it's not cool enough to play with us
919 */
920 continue;
921
922 strdevid = devid_str_encode(curdevid, NULL);
923 /* does this exist in the on-disk cache? */
924 rv = nvlist_lookup_nvlist(mapnvl, strdevid, &newnvl);
925 if (rv == ENOENT) {
926 logmsg(MSG_INFO, "nvlist for %s not found\n", strdevid);
927 /* no, so alloc a new nvl to store it */
928 if (nvlist_alloc(&newnvl, NV_UNIQUE_NAME, 0) != 0) {
929 logmsg(MSG_ERROR,
930 gettext("Unable to allocate space for "
931 "a devid property list: %s\n"),
932 strerror(errno));
933 return (-1);
934 }
935 } else {
936 if ((rv != ENOTSUP) && (rv != EINVAL))
937 logmsg(MSG_INFO,
938 "%s exists in ondisknvl, verifying\n",
939 strdevid);
940 }
941
942 if (popcheck_devnvl(curnode, newnvl, strdevid) != 0) {
943 logmsg(MSG_ERROR,
944 gettext("Unable to populate devid nvpair "
945 "for device with devid %s\n"),
946 strdevid);
947 devid_str_free(strdevid);
948 nvlist_free(newnvl);
949 return (-1);
950 }
951
952 /* Now add newnvl into our cache. */
953 errno = 0;
954 rv = nvlist_add_nvlist(mapnvl, strdevid, newnvl);
955 if (rv) {
956 logmsg(MSG_ERROR,
957 gettext("Unable to add device (devid %s) "
958 "to in-kernel nvl: %s (%d)\n"),
959 strdevid, strerror(rv), rv);
960 devid_str_free(strdevid);
961 nvlist_free(newnvl);
962 return (-1);
963 }
964 logmsg(MSG_INFO,
965 gettext("added device (devid %s) to mapnvl\n\n"),
966 strdevid);
967 devid_str_free(strdevid);
968 }
969 return (0);
970 }
971
972 /*
973 * Operates on a single di_node_t, collecting all the device properties
974 * that we need. devnvl is allocated by the caller, and we add our nvpairs
975 * to it if they don't already exist.
976 *
977 * We are _only_ interested in devices which have a devid. We pull in
978 * devices even when they're excluded via stmsboot -D (driver), because
979 * we don't want to miss out on any devid data that might be handy later.
980 */
981 static int
popcheck_devnvl(di_node_t thisnode,nvlist_t * devnvl,char * strdevid)982 popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl, char *strdevid)
983 {
984 char *path = NULL;
985 char *curpath = NULL;
986 char *devfspath = NULL;
987 char *prop = NULL;
988 int scsivhciparent = 0;
989 int rv = 0;
990 boolean_t mpxenp = B_FALSE;
991
992 errno = 0;
993 devfspath = di_devfs_path(thisnode);
994 if (devfspath == NULL) {
995 logmsg(MSG_ERROR,
996 gettext("Unable to determine devfs path for node: %s\n"),
997 strerror(errno));
998 return (-1);
999 }
1000
1001 /* Add a convenient devfspath to devid inverse map */
1002 if (nvlist_add_string(mapnvl, devfspath, strdevid) != 0) {
1003 logmsg(MSG_ERROR,
1004 gettext("Unable to add device path %s with devid "
1005 "%s to mapnvl\n"), devfspath, strdevid);
1006 return (-1);
1007 }
1008 if (di_prop_lookup_strings(DDI_DEV_T_ANY, di_parent_node(thisnode),
1009 "mpxio-disable", &prop) >= 0) {
1010 if (strncmp(prop, "yes", 3) == 0) {
1011 if (!mpxprop)
1012 mpxprop++;
1013 }
1014 }
1015
1016 if (strncmp(di_driver_name(di_parent_node(thisnode)),
1017 "scsi_vhci", 9) == 0) {
1018 scsivhciparent = 1;
1019 if (!mpxenabled)
1020 mpxenabled++;
1021
1022 rv = nvlist_lookup_boolean_value(devnvl, NVL_MPXEN, &mpxenp);
1023 if (rv || (mpxenp == B_FALSE)) {
1024 rv = nvlist_add_boolean_value(devnvl,
1025 NVL_MPXEN, B_TRUE);
1026 if (rv) {
1027 logmsg(MSG_ERROR,
1028 gettext("Unable to add property %s "
1029 "(set to B_TRUE) for device %s: "
1030 "%s (%d)\n"),
1031 NVL_MPXEN, devfspath,
1032 strerror(rv), rv);
1033 return (-1);
1034 }
1035 logmsg(MSG_INFO, "NVL_MPXEN :: (B_FALSE->B_TRUE)\n");
1036 }
1037 } else {
1038 /* turn _off_ the flag if it was enabled */
1039 rv = nvlist_add_boolean_value(devnvl, NVL_MPXEN, B_FALSE);
1040 if (rv) {
1041 logmsg(MSG_ERROR,
1042 gettext("Unable to add property %s "
1043 "(set to B_FALSE) for device %s: %s (%d)\n"),
1044 NVL_MPXEN, devfspath,
1045 strerror(rv), rv);
1046 return (-1);
1047 }
1048 logmsg(MSG_INFO, "NVL_MPXEN :: (B_TRUE-> B_FALSE)\n");
1049 }
1050
1051 rv = nvlist_add_string(devnvl, NVL_PHYSPATH, devfspath);
1052 if (rv) {
1053 logmsg(MSG_ERROR,
1054 gettext("Unable to add physical device path (%s) "
1055 "property to nvl\n"));
1056 return (-1);
1057 }
1058
1059 if ((curpath = calloc(1, MAXPATHLEN)) == NULL) {
1060 logmsg(MSG_ERROR,
1061 gettext("Unable to allocate space for current path\n"));
1062 return (-1);
1063 }
1064 curpath = find_link(thisnode);
1065 if (curpath == NULL) {
1066 if (readonlyroot) {
1067 return (0);
1068 }
1069 logmsg(MSG_ERROR,
1070 gettext("Unable to determine device path for node %s\n"),
1071 devfspath);
1072 return (-1);
1073 }
1074
1075 rv = nvlist_lookup_string(devnvl, NVL_MPXPATH, &path);
1076
1077 if (scsivhciparent) {
1078 (void) nvlist_add_string(devnvl, NVL_MPXPATH, curpath);
1079 } else {
1080 (void) nvlist_add_string(devnvl, NVL_PATH, curpath);
1081 path = curpath;
1082 }
1083
1084 /*
1085 * This next block provides the path to devid inverse mapping
1086 * that other functions require
1087 */
1088 if (path != NULL) {
1089 if (nvlist_add_string(mapnvl, path, strdevid) != 0) {
1090 logmsg(MSG_ERROR,
1091 gettext("Unable to add device %s with devid "
1092 "%s to mapnvl\n"), path, strdevid);
1093 return (-1);
1094 }
1095 logmsg(MSG_INFO, "popcheck_devnvl: added path %s :: %s\n",
1096 path, strdevid);
1097 }
1098
1099 if (nvlist_add_string(mapnvl, curpath, strdevid) != 0) {
1100 logmsg(MSG_ERROR,
1101 gettext("Unable to add device %s with devid "
1102 "%s to mapnvl: %s\n"),
1103 curpath, strdevid, strerror(errno));
1104 return (-1);
1105 }
1106 logmsg(MSG_INFO, "popcheck_devnvl: added curpath %s :: %s\n",
1107 curpath, strdevid);
1108
1109 return (0);
1110 }
1111
1112 static void
print_mpx_capable(di_node_t curnode)1113 print_mpx_capable(di_node_t curnode)
1114 {
1115 char *prop;
1116 char *path;
1117 char *aliases = NULL;
1118
1119 if (cap_N_option) {
1120 aliases = calloc(1, MAXPATHLEN + 1);
1121 if (aliases == NULL) {
1122 logmsg(MSG_ERROR,
1123 gettext("Unable to allocate memory for a device "
1124 "alias list\n"));
1125 return;
1126 }
1127 }
1128
1129 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
1130 if (di_prop_lookup_strings(DDI_DEV_T_ANY, curnode,
1131 "initiator-port", &prop) >= 0) {
1132 if ((path = di_devfs_path(curnode)) == NULL) {
1133 logmsg(MSG_INFO,
1134 "Unable to find devfs path for device "
1135 "%s: %s\n", &curnode, strerror(errno));
1136 continue;
1137 }
1138 if (cap_N_option) {
1139 char *nodename = di_node_name(curnode);
1140 /* nodename is never going to be null */
1141 if (strstr(aliases, nodename) == NULL)
1142 /* haven't seen this nodename before */
1143 (void) snprintf(aliases,
1144 MAXPATHLEN + 1, "%s|%s",
1145 ((aliases != NULL) ? aliases : ""),
1146 nodename);
1147 } else
1148 (void) printf("%s\n", path);
1149 }
1150 }
1151 if (cap_N_option)
1152 (void) printf("%s\n", aliases);
1153 }
1154
1155 static int
link_cb(di_devlink_t devlink,void * arg)1156 link_cb(di_devlink_t devlink, void *arg)
1157 {
1158 const char *result;
1159
1160 result = di_devlink_path(devlink);
1161 if (result == NULL) {
1162 arg = (void *)"(null)";
1163 } else {
1164 (void) strlcpy(arg, result, strlen(result));
1165 }
1166 logmsg(MSG_INFO, "\nlink_cb::linkdata->resultstr = %s\n",
1167 ((result != NULL) ? result : "(null)"));
1168 return (DI_WALK_CONTINUE);
1169 }
1170
1171 static char *
find_link(di_node_t cnode)1172 find_link(di_node_t cnode)
1173 {
1174 di_minor_t devminor = DI_MINOR_NIL;
1175 di_devlink_handle_t hdl;
1176 char *devfspath = NULL;
1177 char *minorpath = NULL;
1178 char *linkname = NULL;
1179 char *cbresult = NULL;
1180
1181 devfspath = di_devfs_path(cnode);
1182 if (cnode == DI_NODE_NIL) {
1183 logmsg(MSG_ERROR,
1184 gettext("find_ctrl must be called with non-null "
1185 "di_node_t\n"));
1186 return (NULL);
1187 }
1188 logmsg(MSG_INFO, "find_link: devfspath %s\n", devfspath);
1189
1190 if (((cbresult = calloc(1, MAXPATHLEN)) == NULL) ||
1191 ((minorpath = calloc(1, MAXPATHLEN)) == NULL) ||
1192 ((linkname = calloc(1, MAXPATHLEN)) == NULL)) {
1193 logmsg(MSG_ERROR, "unable to allocate space for dev link\n");
1194 return (NULL);
1195 }
1196
1197 devminor = di_minor_next(cnode, devminor);
1198 hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK);
1199 if (hdl == NULL) {
1200 logmsg((readonlyroot ? MSG_INFO : MSG_ERROR),
1201 gettext("unable to take devlink snapshot: %s\n"),
1202 strerror(errno));
1203 return (NULL);
1204 }
1205
1206 linkname = "^dsk/";
1207 (void) snprintf(minorpath, MAXPATHLEN, "%s:c", devfspath);
1208
1209 errno = 0;
1210 if (di_devlink_walk(hdl, linkname, minorpath, DI_PRIMARY_LINK,
1211 (void *)cbresult, link_cb) < 0) {
1212 logmsg(MSG_ERROR,
1213 gettext("Unable to walk devlink snapshot for %s: %s\n"),
1214 minorpath, strerror(errno));
1215 return (NULL);
1216 }
1217
1218 if (di_devlink_fini(&hdl) < 0) {
1219 logmsg(MSG_ERROR,
1220 gettext("Unable to close devlink snapshot: %s\n"),
1221 strerror(errno));
1222 }
1223 if (strstr(cbresult, "dsk/") == NULL)
1224 return (devfspath);
1225
1226 bzero(minorpath, MAXPATHLEN);
1227 /* strip off the trailing "s2" */
1228 bcopy(cbresult, minorpath, strlen(cbresult) - 1);
1229 /* Now strip off the /dev/dsk/ prefix for output flexibility */
1230 linkname = strrchr(minorpath, '/');
1231 return (++linkname);
1232 }
1233
1234 /*
1235 * handle case where device has been probed but its target driver is not
1236 * attached so enumeration has not quite finished. Opening the /devices
1237 * pathname will force the kernel to finish the enumeration process and
1238 * let us get the data we need.
1239 */
1240 static void
get_devid(di_node_t node,ddi_devid_t * thisdevid)1241 get_devid(di_node_t node, ddi_devid_t *thisdevid)
1242 {
1243 int fd;
1244 char realpath[MAXPATHLEN];
1245 char *openpath = di_devfs_path(node);
1246
1247 errno = 0;
1248 bzero(realpath, MAXPATHLEN);
1249 if (strstr(openpath, "/devices") == NULL) {
1250 (void) snprintf(realpath, MAXPATHLEN,
1251 "/devices%s:c,raw", openpath);
1252 fd = open(realpath, O_RDONLY|O_NDELAY);
1253 } else {
1254 fd = open(openpath, O_RDONLY|O_NDELAY);
1255 }
1256
1257 if (fd < 0) {
1258 logmsg(MSG_INFO, "Unable to open path %s: %s\n",
1259 openpath, strerror(errno));
1260 return;
1261 }
1262
1263 if (devid_get(fd, thisdevid) != 0) {
1264 logmsg(MSG_INFO,
1265 "'%s' node (%s) without a devid registered\n",
1266 di_driver_name(node), di_devfs_path(node));
1267 }
1268 (void) close(fd);
1269 }
1270
1271 static int
print_bootpath()1272 print_bootpath()
1273 {
1274 char *bootprop = NULL;
1275
1276 if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root,
1277 "bootpath", &bootprop) >= 0) {
1278 (void) printf("%s\n", bootprop);
1279 return (0);
1280 } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root,
1281 "boot-path", &bootprop) >= 0) {
1282 (void) printf("%s\n", bootprop);
1283 return (0);
1284 } else {
1285 (void) printf("ERROR: no bootpath/boot-path property found\n");
1286 return (ENOENT);
1287 }
1288 }
1289
1290 static void
get_phci_driver_name(char * phci_path,char ** driver_name)1291 get_phci_driver_name(char *phci_path, char **driver_name)
1292 {
1293 di_node_t phci_node = DI_NODE_NIL;
1294 char *tmp = NULL;
1295
1296 phci_node = di_init(phci_path, DINFOCPYONE);
1297 if (phci_node == DI_NODE_NIL) {
1298 logmsg(MSG_ERROR,
1299 gettext("Unable to take phci snapshot "
1300 "(%s: %d)\n"), strerror(errno), errno);
1301 return;
1302 }
1303 tmp = di_driver_name(phci_node);
1304 if (tmp != NULL) {
1305 (void) strncpy(*driver_name, tmp, 10);
1306 }
1307 di_fini(phci_node);
1308 }
1309
1310 /*
1311 * We only call this routine if we have a scsi_vhci node and must
1312 * determine the actual physical path of its first online client
1313 * path.
1314 */
1315 static void
vhci_to_phci(char * devpath,char * slice,int d_flag)1316 vhci_to_phci(char *devpath, char *slice, int d_flag)
1317 {
1318 sv_iocdata_t ioc;
1319 sv_path_info_t *pi;
1320 int vhci_fd;
1321 int rv;
1322 uint_t npaths = 0;
1323 char nodename[MAXPATHLEN];
1324 char *phci_driver = NULL;
1325
1326 vhci_fd = open(VHCI_CTL_NODE, O_RDWR);
1327 if (vhci_fd < 0)
1328 goto failure;
1329
1330 bzero(&ioc, sizeof (sv_iocdata_t));
1331 ioc.client = devpath;
1332 ioc.ret_elem = &npaths;
1333 rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc);
1334 if (rv || npaths == 0) {
1335 logmsg(MSG_INFO,
1336 "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() failed, "
1337 "%s (%d)\n", strerror(rv), rv);
1338 goto failure;
1339 }
1340
1341 bzero(&ioc, sizeof (sv_iocdata_t));
1342 ioc.client = devpath;
1343 ioc.buf_elem = npaths;
1344 ioc.ret_elem = &npaths;
1345 if ((ioc.ret_buf = calloc(npaths, sizeof (sv_path_info_t)))
1346 == NULL)
1347 goto failure;
1348 rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc);
1349 if (rv || npaths == 0) {
1350 logmsg(MSG_INFO,
1351 "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() (#2) "
1352 "failed, %s (%d)\n", strerror(rv), rv);
1353 free(ioc.ret_buf);
1354 goto failure;
1355 }
1356
1357 if (ioc.buf_elem < npaths)
1358 npaths = ioc.buf_elem;
1359
1360 phci_driver = malloc(10);
1361 if (phci_driver == NULL) {
1362 logmsg(MSG_INFO,
1363 "vhci_to_phci: Memory allocation failed\n");
1364 free(ioc.ret_buf);
1365 goto failure;
1366 }
1367
1368 pi = (sv_path_info_t *)ioc.ret_buf;
1369 while (npaths--) {
1370 bzero(nodename, MAXPATHLEN);
1371 bzero(phci_driver, 10);
1372
1373 get_phci_driver_name(pi->device.ret_phci,
1374 &phci_driver);
1375 logmsg(MSG_INFO, "phci driver name: %s\n", phci_driver);
1376 /*
1377 * A hack, but nicer than a platform-specific ifdef
1378 * fp on SPARC using "ssd" as nodename
1379 * mpt use "sd" when mpxio disabled, use "disk" when
1380 * mpxio is enabled
1381 * for alll other cases, "disk" should be used as the
1382 * nodename
1383 */
1384 if (strstr(devpath, "ssd") != NULL) {
1385 (void) snprintf(nodename, 5, "ssd");
1386 } else if (strncmp(phci_driver, "mpt", 10) == 0) {
1387 (void) snprintf(nodename, 5, "sd");
1388 } else {
1389 (void) snprintf(nodename, 5, "disk");
1390 }
1391 if ((d_flag == DISPLAY_ONE_PATH) &&
1392 (pi->ret_state == MDI_PATHINFO_STATE_ONLINE)) {
1393 (void) printf("%s/%s@%s", pi->device.ret_phci,
1394 nodename, pi->ret_addr);
1395 if ((slice != NULL) && (strlen(slice) <= 3)) {
1396 (void) printf("%s\n", slice);
1397 } else {
1398 (void) printf("\n");
1399 }
1400 break;
1401 } else if (d_flag == DISPLAY_ALL_PATH) {
1402 (void) printf("%s/%s@%s", pi->device.ret_phci,
1403 nodename, pi->ret_addr);
1404 if ((slice != NULL) && (strlen(slice) <= 3)) {
1405 (void) printf("%s\n", slice);
1406 } else {
1407 (void) printf("\n");
1408 }
1409 }
1410 pi++;
1411 }
1412 free(ioc.ret_buf);
1413 free(phci_driver);
1414 return;
1415
1416 failure:
1417 (void) printf("NOT_MAPPED\n");
1418 }
1419
1420 /*
1421 * Write /etc/vfstab to /etc/vfstab.new, with any remapped device
1422 * names substituted.
1423 *
1424 * Returns:
1425 * 0 successful operation
1426 * -1 failed
1427 */
1428 static int
update_vfstab()1429 update_vfstab()
1430 {
1431 FILE *fdin, *fdout;
1432 char *buf, *tmpbuf;
1433 char fname[MAXPATHLEN];
1434 int rv = -1, rval = -1;
1435 char cdev[MAXPATHLEN];
1436 char bdev[MAXPATHLEN];
1437 char mntpt[MAXPATHLEN];
1438 char fstype[512];
1439 char fsckpass[512];
1440 char mntboot[512];
1441 char mntopt[MAXPATHLEN];
1442 char fmt[80];
1443 char *prefixt = NULL;
1444 char *curdev = NULL;
1445 char *thisdevid = NULL;
1446 char *slice = NULL;
1447 nvlist_t *thisdev;
1448 boolean_t devmpx = B_FALSE;
1449
1450 buf = calloc(1, MAXPATHLEN);
1451 tmpbuf = calloc(1, MAXPATHLEN);
1452 if (buf == NULL || tmpbuf == NULL)
1453 return (-1);
1454
1455 (void) snprintf(fname, MAXPATHLEN, "/etc/mpxio/vfstab.new");
1456
1457 fdin = fopen("/etc/vfstab", "r");
1458 fdout = fopen(fname, "w+");
1459 if (fdin == NULL || fdout == NULL) {
1460 logmsg(MSG_INFO, "Unable to open vfstab or create a backup "
1461 "vfstab %s\n");
1462 return (-1);
1463 }
1464
1465 (void) snprintf(fmt, sizeof (fmt),
1466 "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1,
1467 sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1,
1468 sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1);
1469
1470 while (fgets(buf, MAXPATHLEN, fdin) != NULL) {
1471 if (strlen(buf) == (MAXPATHLEN - 1) &&
1472 buf[MAXPATHLEN-2] != '\n') {
1473 logmsg(MSG_ERROR,
1474 gettext("/etc/vfstab line length too long, "
1475 "exceeded %2$d: \"%3$s\"\n"),
1476 MAXPATHLEN - 2, buf);
1477 goto out;
1478 }
1479
1480 prefixt = NULL;
1481 curdev = NULL;
1482 slice = NULL;
1483 thisdevid = NULL;
1484 thisdev = NULL;
1485
1486 /* LINTED - variable format specifier */
1487 rv = sscanf(buf, fmt, bdev, cdev, mntpt, fstype, fsckpass,
1488 mntboot, mntopt);
1489
1490 /*
1491 * Walk through the lines in the input file (/etc/vfstab),
1492 * skipping anything which is _not_ a COGD (common or garden
1493 * disk), ie all the /devices, /system, /dev/md, /dev/vx and
1494 * /dev/zvol and so forth.
1495 */
1496 if ((rv == 7) && (bdev[0] == '/') &&
1497 (strstr(bdev, "/dev/dsk"))) {
1498 slice = strrchr(bdev, 's');
1499 /* take a copy, strip off /dev/dsk/ */
1500 prefixt = strrchr(bdev, 'c');
1501 prefixt[strlen(bdev) - 9 - strlen(slice)] = '\0';
1502 slice++; /* advance past the s */
1503 rval = nvlist_lookup_string(mapnvl, prefixt,
1504 &thisdevid);
1505 if (rval) {
1506 /* Whoa, where did this device go?! */
1507 logmsg(MSG_INFO,
1508 "error looking up device %s\n", prefixt);
1509 /* Comment-out this line in the new version */
1510 (void) snprintf(tmpbuf, MAXPATHLEN,
1511 "# DEVICE NOT FOUND %s", buf);
1512 (void) fprintf(fdout, "%s", tmpbuf);
1513 continue;
1514 } else {
1515 /* The device exists in our mapnvl */
1516 (void) nvlist_lookup_nvlist(mapnvl, thisdevid,
1517 &thisdev);
1518 (void) nvlist_lookup_boolean_value(thisdev,
1519 NVL_MPXEN, &devmpx);
1520 (void) nvlist_lookup_string(thisdev,
1521 ((devmpx == B_TRUE)
1522 ? NVL_MPXPATH : NVL_PATH),
1523 &curdev);
1524 }
1525 }
1526
1527 if ((prefixt != NULL) && (curdev != NULL) &&
1528 (rv = (strncmp(prefixt, curdev, strlen(prefixt)) != 0))) {
1529 /* Mapping change for this device */
1530 if (strcmp(fstype, "swap") == 0) {
1531 (void) snprintf(tmpbuf, MAXPATHLEN,
1532 "/dev/dsk/%ss%s\t-\t-\tswap\t"
1533 "%s\t%s\t%s\n",
1534 curdev, slice, fsckpass, mntboot, mntopt);
1535 } else {
1536 (void) snprintf(tmpbuf, MAXPATHLEN,
1537 "/dev/dsk/%ss%s\t/dev/rdsk/%ss%s\t"
1538 "%s\t%s\t%s\t%s\t%s\n",
1539 curdev, slice, curdev, slice,
1540 mntpt, fstype, fsckpass, mntboot, mntopt);
1541 }
1542 errno = 0;
1543 (void) fprintf(fdout, "%s", tmpbuf);
1544 } else {
1545 (void) fprintf(fdout, "%s", buf);
1546 }
1547
1548 errno = 0;
1549 if (fflush(fdout) != 0) {
1550 logmsg(MSG_ERROR,
1551 gettext("fprintf failed to write to %s: %s (%d)\n"),
1552 fname, strerror(errno), errno);
1553 goto out;
1554 }
1555 }
1556 out:
1557 (void) fclose(fdin);
1558 (void) fclose(fdout);
1559 free(buf);
1560 free(tmpbuf);
1561 return (errno);
1562 }
1563