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 int 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 if (strlcpy(drvlimit, optarg, MAXMODCONFNAME) >=
413 MAXMODCONFNAME) {
414 logmsg(MSG_ERROR,
415 gettext("invalid parent driver (%s) "
416 "specified"), optarg);
417 usage();
418 }
419 /* update this if adding support for a new driver */
420 if (strcmp(drvlimit, "fp") != 0 &&
421 strcmp(drvlimit, "mpt") != 0 &&
422 strcmp(drvlimit, "mpt_sas") != 0 &&
423 strcmp(drvlimit, "pmcs") != 0) {
424 logmsg(MSG_ERROR,
425 gettext("invalid parent driver (%s) "
426 "specified"), drvlimit);
427 usage();
428 }
429 break;
430 case 'h':
431 /* Just drop out and print the usage() output */
432 globarg = MPX_USAGE;
433 break;
434 case 'i':
435 globarg = MPX_INIT;
436 break;
437 case 'l':
438 globarg |= MPX_LIST;
439 limctrl = (int)atol(optarg);
440 if (limctrl < 0) {
441 logmsg(MSG_INFO,
442 gettext("invalid controller number "
443 "(%d), checking all controllers\n"),
444 limctrl);
445 }
446 break;
447 case 'L':
448 globarg |= MPX_LIST;
449 break;
450 case 'g':
451 guid = 1;
452 break;
453 case 'm':
454 globarg = MPX_MAP;
455 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
456 logmsg(MSG_ERROR,
457 gettext("Unable to allocate space for a "
458 "device name\n"));
459 exit(errno);
460 }
461 devicep = strdup(optarg);
462 break;
463 case 'N':
464 cap_N_option = 1;
465 globarg = MPX_CAPABLE_CTRL;
466 break;
467 case 'n':
468 globarg = MPX_CAPABLE_CTRL;
469 break;
470 case 'o':
471 globarg = MPX_GETPATH;
472 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
473 logmsg(MSG_ERROR,
474 gettext("Unable to allocate space for a "
475 "device name\n"));
476 exit(errno);
477 }
478 devicep = strdup(optarg);
479 break;
480 case 'p':
481 globarg = MPX_PHYSICAL;
482 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
483 logmsg(MSG_ERROR,
484 gettext("Unable to allocate space for a "
485 "device name\n"));
486 exit(errno);
487 }
488 devicep = strdup(optarg);
489 break;
490 case 'q':
491 globarg = MPX_DEV_PATH;
492 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
493 logmsg(MSG_ERROR,
494 gettext("Unable to allocate space for a "
495 "device name\n"));
496 exit(errno);
497 }
498 devicep = strdup(optarg);
499 break;
500 case 'u':
501 globarg = MPX_UPDATEVFSTAB;
502 break;
503 default:
504 logmsg(MSG_ERROR,
505 gettext("Invalid command line option (%c)\n"),
506 opt);
507 usage();
508 }
509 }
510
511 if ((globarg >= MPX_USAGE) || (guid && (globarg != MPX_LIST)))
512 usage();
513
514 if ((drvlimit != NULL) &&
515 ((globarg != MPX_LIST) &&
516 (globarg != MPX_CAPABLE_CTRL)))
517 usage();
518 }
519
520 static void
logmsg(int level,const char * msg,...)521 logmsg(int level, const char *msg, ...)
522 {
523 va_list ap;
524
525 if ((level >= MSG_ERROR) ||
526 ((debugflag > 0) && (level >= MSG_INFO))) {
527 (void) fprintf(stdout, "stmsboot: ");
528 va_start(ap, msg);
529 (void) vfprintf(stdout, msg, ap);
530 va_end(ap);
531 }
532 }
533
534 /*
535 * It's up to the caller to do any sorting or pretty-printing of the device
536 * mappings we report. Since we're storing the device links as just the cXtYdZ
537 * part, we'll add /dev/rdsk/ back on when we print the listing so we maintain
538 * compatibility with previous versions of this tool. There's a little bit
539 * of footwork involved to make sure that we show all the paths to a device
540 * rather than just the first one we stashed away.
541 */
542 static void
list_devs(int listguids,int ctrl)543 list_devs(int listguids, int ctrl)
544 {
545 nvlist_t *thisdevnvl;
546 nvpair_t *pair;
547 char *diskpath, *livepath, *key, *querydev;
548 char *matchctrl = NULL;
549 char checkctrl[MAXPATHLEN];
550 int rv;
551
552 if (!mpxenabled) {
553 if (mpxprop) {
554 logmsg(MSG_ERROR, gettext("MPXIO disabled\n"));
555 } else {
556 logmsg(MSG_ERROR, gettext("No STMS devices have "
557 "been found\n"));
558 }
559 return;
560 }
561
562 if (listguids) {
563 (void) printf(gettext("non-STMS device name\t\t\tGUID\n"
564 "------------------------------------------"
565 "------------------------\n"));
566 } else {
567 (void) printf(gettext("non-STMS device name\t\t\t"
568 "STMS device name\n"
569 "------------------------------------------"
570 "------------------------\n"));
571 }
572
573 bzero(checkctrl, MAXPATHLEN);
574 pair = NULL;
575 while ((pair = nvlist_next_nvpair(mapnvl, pair))
576 != NULL) {
577 boolean_t livescsivhcip = B_FALSE;
578
579 if ((((rv = nvpair_value_string(pair, &querydev)) < 0) ||
580 ((key = nvpair_name(pair)) == NULL)) ||
581 ((strstr(key, "/pci") != NULL) ||
582 (strstr(key, "/sbus") != NULL) ||
583 (strstr(key, "/scsi_vhci") != NULL) ||
584 (strncmp(key, "id1", 3) == 0))) {
585 logmsg(MSG_INFO,
586 "list_devs: rv = %d; (%s) is not a devlink, "
587 "continuing.\n", rv,
588 (key != NULL) ? key : "null");
589 querydev = NULL;
590 continue;
591 }
592
593 (void) nvlist_lookup_nvlist(mapnvl, querydev, &thisdevnvl);
594 (void) nvlist_lookup_boolean_value(thisdevnvl, NVL_MPXEN,
595 &livescsivhcip);
596 (void) nvlist_lookup_string(thisdevnvl, NVL_MPXPATH,
597 &livepath);
598
599 if ((!livescsivhcip) ||
600 (livescsivhcip &&
601 (strncmp(key, livepath, strlen(key)) == 0)))
602 continue;
603
604 (void) nvlist_lookup_string(thisdevnvl, NVL_PATH,
605 &diskpath);
606
607 logmsg(MSG_INFO,
608 "list_devs: %s :: %s ::%s :: MPXEN (%s)\n",
609 key, diskpath, livepath,
610 ((livescsivhcip) ? "TRUE" : "FALSE"));
611
612 if (ctrl > -1) {
613 (void) sprintf(checkctrl, "c%dt", ctrl);
614 matchctrl = strstr(key, checkctrl);
615 if (matchctrl == NULL)
616 continue;
617 }
618 if (listguids != 0) {
619 char *tempguid;
620 ddi_devid_t curdevid;
621 int rv;
622
623 rv = devid_str_decode(querydev, &curdevid, NULL);
624 if (rv == -1) {
625 logmsg(MSG_INFO, "Unable to decode devid %s\n",
626 key);
627 continue;
628 }
629 tempguid = devid_to_guid(curdevid);
630 if (tempguid != NULL)
631 (void) printf("/dev/rdsk/%s\t%s\n",
632 diskpath, tempguid);
633
634 devid_free_guid(tempguid);
635 devid_free(curdevid);
636 continue;
637 }
638
639 (void) printf("/dev/rdsk/%s\t/dev/rdsk/%s\n",
640 (strstr(key, diskpath) == NULL) ? key : diskpath,
641 livepath);
642 }
643 }
644
645 /*
646 * We get passed a device name which we search the mapnvl for. If we find
647 * it, we print the mapping as it is found. It is up to the caller of this
648 * utility to do any pretty-printing of the results. If a device listed on
649 * the command line does not exist in the mapnvl, then we print NOT_MAPPED.
650 * Otherwise we print the command-line device name as it maps to what is
651 * stashed in the mapnvl - even if that's a "no change" device mapping.
652 *
653 * Example output (-p maps to physpath=BOOT)
654 * # /lib/mpxio/stmsboot_util -p \
655 * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a
656 * /scsi_vhci/disk@g500000e011e17720:a
657 *
658 * Or the reverse:
659 * # /lib/mpxio/stmsboot_util -p /scsi_vhci/disk@g500000e011e17720:a
660 * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a
661 *
662 * For the -m option, used when we're trying to find the root device mapping:
663 *
664 * # /lib/mpxio/stmsboot_util -m /dev/dsk/c2t0d0s2
665 * /dev/dsk/c3t500000E011637CF0d0s2
666 */
667 static void
report_map(char * argdev,int physpath)668 report_map(char *argdev, int physpath)
669 {
670 nvlist_t *thisdev;
671 int rv = 0;
672 char *thisdevid;
673 char *mpxpath = NULL;
674 char *prefixt = NULL;
675 char *prefixp = NULL;
676 char *stripdev = NULL;
677 char *slice = NULL;
678 boolean_t mpxenp;
679 uint_t slicelen = 0;
680
681 mpxenp = B_FALSE;
682
683 if ((prefixt = calloc(1, strlen(argdev) + 1)) == NULL) {
684 logmsg(MSG_INFO, "Unable to allocate memory\n");
685 (void) printf("NOT_MAPPED\n");
686 return;
687 }
688
689 (void) strlcpy(prefixt, argdev, strlen(argdev) + 1);
690
691 slice = strrchr(argdev, (physpath == NONBOOT) ? 's' : ':');
692 if (slice != NULL) {
693 slicelen = strlen(slice);
694 if (slicelen > 3)
695 /* invalid size - max is 3 chars */
696 slicelen = 0;
697 }
698
699 if ((stripdev = calloc(1, strlen(prefixt) + 1)) == NULL) {
700 logmsg(MSG_INFO, "Unable to allocate memory\n");
701 (void) printf("NOT_MAPPED\n");
702 free(prefixt);
703 return;
704 }
705
706 if ((strstr(prefixt, "/scsi_vhci") == NULL) &&
707 (strstr(prefixt, "/pci") == NULL) &&
708 (strstr(prefixt, "/sbus") == NULL)) {
709 prefixp = strrchr(prefixt, '/');
710 (void) strlcpy(stripdev,
711 (prefixp == NULL) ? prefixt : prefixp + 1,
712 (prefixp == NULL) ?
713 strlen(prefixt) + 1: strlen(prefixp) + 1);
714 if (prefixp != NULL)
715 prefixt[strlen(argdev) - strlen(prefixp) + 1] = '\0';
716 } else {
717 if ((physpath != BOOT) &&
718 (physpath != BOOT_PATH)) {
719 logmsg(MSG_INFO, "Invalid device path provided\n");
720 (void) printf("NOT_MAPPED\n");
721 free(stripdev);
722 free(prefixt);
723 return;
724 }
725 (void) strlcpy(stripdev, argdev, strlen(argdev) + 1);
726 }
727
728 logmsg(MSG_INFO,
729 "stripdev (%s), prefixt(%s), prefixp(%s), slice(%s)\n",
730 (stripdev == NULL) ? "null" : stripdev,
731 (prefixt == NULL) ? "null" : prefixt,
732 (prefixp == NULL) ? "null" : prefixp,
733 (slice == NULL) ? "null" : slice);
734
735 if (slicelen > 0)
736 stripdev[strlen(stripdev) - slicelen] = '\0';
737
738 /* search for the shortened version */
739 rv = nvlist_lookup_string(mapnvl, stripdev, &thisdevid);
740 if (rv) {
741 if ((physpath != BOOT) &&
742 (physpath != BOOT_PATH)) {
743 logmsg(MSG_INFO,
744 "searched mapnvl for '%s', got %s (%d)\n",
745 stripdev, strerror(rv), rv);
746 (void) printf("NOT_MAPPED\n");
747 free(stripdev);
748 free(prefixt);
749 return;
750 }
751 }
752
753 logmsg(MSG_INFO, "device %s has devid %s\n", stripdev, thisdevid);
754
755 if (nvlist_lookup_nvlist(mapnvl, thisdevid, &thisdev) != 0) {
756 logmsg(MSG_INFO, "device (%s) in mapnvl but "
757 "not mapped!\n", thisdevid);
758 (void) printf("NOT_MAPPED\n");
759 free(stripdev);
760 free(prefixt);
761 return;
762 }
763
764 /* quick exit */
765 if (!mpxenabled && (strstr(argdev, "/pci") != NULL ||
766 strstr(argdev, "/sbus") != NULL)) {
767 (void) printf("%s\n", argdev);
768 free(stripdev);
769 free(prefixt);
770 return;
771 }
772
773 (void) nvlist_lookup_boolean_value(thisdev, NVL_MPXEN, &mpxenp);
774
775 if (physpath == BOOT) {
776 (void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath);
777 if ((strstr(argdev, "/scsi_vhci") != NULL) &&
778 (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) {
779 /* Need to translate vhci to phci */
780 vhci_to_phci(stripdev, slice, DISPLAY_ONE_PATH);
781 } else {
782 (void) printf("%s%s\n", mpxpath,
783 ((slicelen > 0) && slice != NULL) ? slice : "");
784 }
785 } else if (physpath == BOOT_PATH) {
786 (void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath);
787 if ((strstr(argdev, "/scsi_vhci") != NULL) &&
788 (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) {
789 /* Need to translate vhci to phci */
790 vhci_to_phci(stripdev, slice, DISPLAY_ALL_PATH);
791 } else {
792 (void) printf("%s%s\n", mpxpath,
793 ((slicelen > 0) && slice != NULL) ? slice : "");
794 }
795 } else {
796 (void) nvlist_lookup_string(thisdev,
797 ((readonlyroot) ? NVL_PHYSPATH :
798 ((mpxenp == B_TRUE) ? NVL_MPXPATH : NVL_PATH)),
799 &mpxpath);
800 logmsg(MSG_INFO, "mpxpath = %s\n",
801 (mpxpath == NULL) ? "null" : mpxpath);
802 if (readonlyroot ||
803 (strstr(mpxpath, "/scsi_vhci") != NULL) ||
804 (strstr(mpxpath, "/pci") != NULL) ||
805 (strstr(mpxpath, "/sbus") != NULL)) {
806 /*
807 * If we see a physical path here it means that
808 * devlinks aren't fully initialised yet, so we
809 * are still in maintenance/single-user mode.
810 */
811 (void) printf("/devices%s:%c\n", mpxpath,
812 slice[1] + '1');
813 } else {
814 (void) printf("%s%s%s\n",
815 (prefixt[0] == '/') ? prefixt : "",
816 mpxpath,
817 ((slicelen > 0) && slice != NULL) ? slice : "");
818 }
819 }
820 free(prefixt);
821 free(stripdev);
822 }
823
824 /*
825 * Validate the in-kernel and on-disk forms of our devid cache,
826 * returns -1 for unfixable error and 0 for success.
827 */
828 static int
validate_devnvl()829 validate_devnvl()
830 {
831 di_node_t curnode;
832 int rv1 = -1;
833 int rv2 = -1;
834
835 /*
836 * Method: we walk through the kernel's concept of the device tree
837 * looking for "ssd" then "sd" nodes.
838 * We check to see whether the device's devid is already in our nvlist
839 * (on disk) nvlist cache file. If it is, we check that it's components
840 * match what we've got already and fill any missing fields.
841 * If the devid isn't in our on-disk nvlist already then we add it
842 * and populate the property nvpairs.
843 *
844 * At the end of this function we should have this program's concept
845 * of the devid-keyed nvlist matching what is in the ondisk form which
846 * is ready to be written out.
847 * If we can't do this, then we return -1.
848 */
849 curnode = di_drv_first_node("ssd", devinfo_root);
850 if (curnode != DI_NODE_NIL)
851 rv1 = mpxio_nvl_boilerplate(curnode);
852
853 curnode = di_drv_first_node("sd", devinfo_root);
854 if (curnode != DI_NODE_NIL)
855 rv2 = mpxio_nvl_boilerplate(curnode);
856
857 if (rv1 + rv2 == -2)
858 return (-1);
859
860 return (0);
861 }
862
863 /*
864 * According to devfs path name, it will print device node name.
865 */
866 static void
print_node_name(char * drv_name,char * strdevfspath)867 print_node_name(char *drv_name, char *strdevfspath)
868 {
869 di_node_t curnode;
870 char *devfspath = NULL;
871 char *node_name = NULL;
872
873 curnode = di_drv_first_node(drv_name, devinfo_root);
874 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
875 devfspath = di_devfs_path(curnode);
876 logmsg(MSG_INFO, "find: devfspath %s\n", devfspath);
877
878 if (devfspath == NULL)
879 continue;
880
881 if ((strlen(strdevfspath) == strlen(devfspath)) &&
882 (strncmp(strdevfspath, devfspath,
883 strlen(devfspath)) == 0)) {
884 node_name = find_link(curnode);
885 if (node_name == NULL) {
886 (void) printf("NOT MAPPED\n");
887 } else {
888 (void) printf("%s\n", node_name);
889 }
890 return;
891 }
892 }
893 }
894
895 /*
896 * report device node name, search "ssd" and "sd" nodes,
897 * print the device node name which device path is same as
898 * parameter.
899 */
900 static void
report_dev_node_name(char * strdevfspath)901 report_dev_node_name(char *strdevfspath)
902 {
903 logmsg(MSG_INFO, "strdevfspath: %s\n", strdevfspath);
904 print_node_name("ssd", strdevfspath);
905 print_node_name("sd", strdevfspath);
906 }
907
908 static int
mpxio_nvl_boilerplate(di_node_t curnode)909 mpxio_nvl_boilerplate(di_node_t curnode)
910 {
911 int rv;
912 char *strdevid;
913 ddi_devid_t curdevid;
914 nvlist_t *newnvl;
915
916 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
917 errno = 0;
918
919 curdevid = NULL;
920 get_devid(curnode, &curdevid);
921 if (curdevid == NULL)
922 /*
923 * There's no devid registered for this device
924 * so it's not cool enough to play with us
925 */
926 continue;
927
928 strdevid = devid_str_encode(curdevid, NULL);
929 /* does this exist in the on-disk cache? */
930 rv = nvlist_lookup_nvlist(mapnvl, strdevid, &newnvl);
931 if (rv == ENOENT) {
932 logmsg(MSG_INFO, "nvlist for %s not found\n", strdevid);
933 /* no, so alloc a new nvl to store it */
934 if (nvlist_alloc(&newnvl, NV_UNIQUE_NAME, 0) != 0) {
935 logmsg(MSG_ERROR,
936 gettext("Unable to allocate space for "
937 "a devid property list: %s\n"),
938 strerror(errno));
939 return (-1);
940 }
941 } else {
942 if ((rv != ENOTSUP) && (rv != EINVAL))
943 logmsg(MSG_INFO,
944 "%s exists in ondisknvl, verifying\n",
945 strdevid);
946 }
947
948 if (popcheck_devnvl(curnode, newnvl, strdevid) != 0) {
949 logmsg(MSG_ERROR,
950 gettext("Unable to populate devid nvpair "
951 "for device with devid %s\n"),
952 strdevid);
953 devid_str_free(strdevid);
954 nvlist_free(newnvl);
955 return (-1);
956 }
957
958 /* Now add newnvl into our cache. */
959 errno = 0;
960 rv = nvlist_add_nvlist(mapnvl, strdevid, newnvl);
961 if (rv) {
962 logmsg(MSG_ERROR,
963 gettext("Unable to add device (devid %s) "
964 "to in-kernel nvl: %s (%d)\n"),
965 strdevid, strerror(rv), rv);
966 devid_str_free(strdevid);
967 nvlist_free(newnvl);
968 return (-1);
969 }
970 logmsg(MSG_INFO,
971 gettext("added device (devid %s) to mapnvl\n\n"),
972 strdevid);
973 devid_str_free(strdevid);
974 }
975 return (0);
976 }
977
978 /*
979 * Operates on a single di_node_t, collecting all the device properties
980 * that we need. devnvl is allocated by the caller, and we add our nvpairs
981 * to it if they don't already exist.
982 *
983 * We are _only_ interested in devices which have a devid. We pull in
984 * devices even when they're excluded via stmsboot -D (driver), because
985 * we don't want to miss out on any devid data that might be handy later.
986 */
987 static int
popcheck_devnvl(di_node_t thisnode,nvlist_t * devnvl,char * strdevid)988 popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl, char *strdevid)
989 {
990 char *path = NULL;
991 char *curpath = NULL;
992 char *devfspath = NULL;
993 char *prop = NULL;
994 int scsivhciparent = 0;
995 int rv = 0;
996 boolean_t mpxenp = B_FALSE;
997
998 errno = 0;
999 devfspath = di_devfs_path(thisnode);
1000 if (devfspath == NULL) {
1001 logmsg(MSG_ERROR,
1002 gettext("Unable to determine devfs path for node: %s\n"),
1003 strerror(errno));
1004 return (-1);
1005 }
1006
1007 /* Add a convenient devfspath to devid inverse map */
1008 if (nvlist_add_string(mapnvl, devfspath, strdevid) != 0) {
1009 logmsg(MSG_ERROR,
1010 gettext("Unable to add device path %s with devid "
1011 "%s to mapnvl\n"), devfspath, strdevid);
1012 return (-1);
1013 }
1014 if (di_prop_lookup_strings(DDI_DEV_T_ANY, di_parent_node(thisnode),
1015 "mpxio-disable", &prop) >= 0) {
1016 if (strncmp(prop, "yes", 3) == 0) {
1017 if (!mpxprop)
1018 mpxprop++;
1019 }
1020 }
1021
1022 if (strncmp(di_driver_name(di_parent_node(thisnode)),
1023 "scsi_vhci", 9) == 0) {
1024 scsivhciparent = 1;
1025 if (!mpxenabled)
1026 mpxenabled++;
1027
1028 rv = nvlist_lookup_boolean_value(devnvl, NVL_MPXEN, &mpxenp);
1029 if (rv || (mpxenp == B_FALSE)) {
1030 rv = nvlist_add_boolean_value(devnvl,
1031 NVL_MPXEN, B_TRUE);
1032 if (rv) {
1033 logmsg(MSG_ERROR,
1034 gettext("Unable to add property %s "
1035 "(set to B_TRUE) for device %s: "
1036 "%s (%d)\n"),
1037 NVL_MPXEN, devfspath,
1038 strerror(rv), rv);
1039 return (-1);
1040 }
1041 logmsg(MSG_INFO, "NVL_MPXEN :: (B_FALSE->B_TRUE)\n");
1042 }
1043 } else {
1044 /* turn _off_ the flag if it was enabled */
1045 rv = nvlist_add_boolean_value(devnvl, NVL_MPXEN, B_FALSE);
1046 if (rv) {
1047 logmsg(MSG_ERROR,
1048 gettext("Unable to add property %s "
1049 "(set to B_FALSE) for device %s: %s (%d)\n"),
1050 NVL_MPXEN, devfspath,
1051 strerror(rv), rv);
1052 return (-1);
1053 }
1054 logmsg(MSG_INFO, "NVL_MPXEN :: (B_TRUE-> B_FALSE)\n");
1055 }
1056
1057 rv = nvlist_add_string(devnvl, NVL_PHYSPATH, devfspath);
1058 if (rv) {
1059 logmsg(MSG_ERROR,
1060 gettext("Unable to add physical device path (%s) "
1061 "property to nvl\n"));
1062 return (-1);
1063 }
1064
1065 if ((curpath = calloc(1, MAXPATHLEN)) == NULL) {
1066 logmsg(MSG_ERROR,
1067 gettext("Unable to allocate space for current path\n"));
1068 return (-1);
1069 }
1070 curpath = find_link(thisnode);
1071 if (curpath == NULL) {
1072 if (readonlyroot) {
1073 return (0);
1074 }
1075 logmsg(MSG_ERROR,
1076 gettext("Unable to determine device path for node %s\n"),
1077 devfspath);
1078 return (-1);
1079 }
1080
1081 rv = nvlist_lookup_string(devnvl, NVL_MPXPATH, &path);
1082
1083 if (scsivhciparent) {
1084 (void) nvlist_add_string(devnvl, NVL_MPXPATH, curpath);
1085 } else {
1086 (void) nvlist_add_string(devnvl, NVL_PATH, curpath);
1087 path = curpath;
1088 }
1089
1090 /*
1091 * This next block provides the path to devid inverse mapping
1092 * that other functions require
1093 */
1094 if (path != NULL) {
1095 if (nvlist_add_string(mapnvl, path, strdevid) != 0) {
1096 logmsg(MSG_ERROR,
1097 gettext("Unable to add device %s with devid "
1098 "%s to mapnvl\n"), path, strdevid);
1099 return (-1);
1100 }
1101 logmsg(MSG_INFO, "popcheck_devnvl: added path %s :: %s\n",
1102 path, strdevid);
1103 }
1104
1105 if (nvlist_add_string(mapnvl, curpath, strdevid) != 0) {
1106 logmsg(MSG_ERROR,
1107 gettext("Unable to add device %s with devid "
1108 "%s to mapnvl: %s\n"),
1109 curpath, strdevid, strerror(errno));
1110 return (-1);
1111 }
1112 logmsg(MSG_INFO, "popcheck_devnvl: added curpath %s :: %s\n",
1113 curpath, strdevid);
1114
1115 return (0);
1116 }
1117
1118 static void
print_mpx_capable(di_node_t curnode)1119 print_mpx_capable(di_node_t curnode)
1120 {
1121 char *prop;
1122 char *path;
1123 char *aliases = NULL;
1124
1125 if (cap_N_option) {
1126 aliases = calloc(1, MAXPATHLEN + 1);
1127 if (aliases == NULL) {
1128 logmsg(MSG_ERROR,
1129 gettext("Unable to allocate memory for a device "
1130 "alias list\n"));
1131 return;
1132 }
1133 }
1134
1135 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
1136 if (di_prop_lookup_strings(DDI_DEV_T_ANY, curnode,
1137 "initiator-port", &prop) >= 0) {
1138 if ((path = di_devfs_path(curnode)) == NULL) {
1139 logmsg(MSG_INFO,
1140 "Unable to find devfs path for device "
1141 "%s: %s\n", &curnode, strerror(errno));
1142 continue;
1143 }
1144 if (cap_N_option) {
1145 char *nodename = di_node_name(curnode);
1146 /* nodename is never going to be null */
1147 if (strstr(aliases, nodename) == NULL)
1148 /* haven't seen this nodename before */
1149 (void) snprintf(aliases,
1150 MAXPATHLEN + 1, "%s|%s",
1151 ((aliases != NULL) ? aliases : ""),
1152 nodename);
1153 } else
1154 (void) printf("%s\n", path);
1155 }
1156 }
1157 if (cap_N_option)
1158 (void) printf("%s\n", aliases);
1159 }
1160
1161 static int
link_cb(di_devlink_t devlink,void * arg)1162 link_cb(di_devlink_t devlink, void *arg)
1163 {
1164 const char *result;
1165
1166 result = di_devlink_path(devlink);
1167 if (result == NULL) {
1168 arg = (void *)"(null)";
1169 } else {
1170 (void) strlcpy(arg, result, strlen(result));
1171 }
1172 logmsg(MSG_INFO, "\nlink_cb::linkdata->resultstr = %s\n",
1173 ((result != NULL) ? result : "(null)"));
1174 return (DI_WALK_CONTINUE);
1175 }
1176
1177 static char *
find_link(di_node_t cnode)1178 find_link(di_node_t cnode)
1179 {
1180 di_minor_t devminor = DI_MINOR_NIL;
1181 di_devlink_handle_t hdl;
1182 char *devfspath = NULL;
1183 char *minorpath = NULL;
1184 char *linkname = NULL;
1185 char *cbresult = NULL;
1186
1187 devfspath = di_devfs_path(cnode);
1188 if (cnode == DI_NODE_NIL) {
1189 logmsg(MSG_ERROR,
1190 gettext("find_ctrl must be called with non-null "
1191 "di_node_t\n"));
1192 return (NULL);
1193 }
1194 logmsg(MSG_INFO, "find_link: devfspath %s\n", devfspath);
1195
1196 if (((cbresult = calloc(1, MAXPATHLEN)) == NULL) ||
1197 ((minorpath = calloc(1, MAXPATHLEN)) == NULL) ||
1198 ((linkname = calloc(1, MAXPATHLEN)) == NULL)) {
1199 logmsg(MSG_ERROR, "unable to allocate space for dev link\n");
1200 return (NULL);
1201 }
1202
1203 devminor = di_minor_next(cnode, devminor);
1204 hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK);
1205 if (hdl == NULL) {
1206 logmsg((readonlyroot ? MSG_INFO : MSG_ERROR),
1207 gettext("unable to take devlink snapshot: %s\n"),
1208 strerror(errno));
1209 return (NULL);
1210 }
1211
1212 linkname = "^dsk/";
1213 (void) snprintf(minorpath, MAXPATHLEN, "%s:c", devfspath);
1214
1215 errno = 0;
1216 if (di_devlink_walk(hdl, linkname, minorpath, DI_PRIMARY_LINK,
1217 (void *)cbresult, link_cb) < 0) {
1218 logmsg(MSG_ERROR,
1219 gettext("Unable to walk devlink snapshot for %s: %s\n"),
1220 minorpath, strerror(errno));
1221 return (NULL);
1222 }
1223
1224 if (di_devlink_fini(&hdl) < 0) {
1225 logmsg(MSG_ERROR,
1226 gettext("Unable to close devlink snapshot: %s\n"),
1227 strerror(errno));
1228 }
1229 if (strstr(cbresult, "dsk/") == NULL)
1230 return (devfspath);
1231
1232 bzero(minorpath, MAXPATHLEN);
1233 /* strip off the trailing "s2" */
1234 bcopy(cbresult, minorpath, strlen(cbresult) - 1);
1235 /* Now strip off the /dev/dsk/ prefix for output flexibility */
1236 linkname = strrchr(minorpath, '/');
1237 return (++linkname);
1238 }
1239
1240 /*
1241 * handle case where device has been probed but its target driver is not
1242 * attached so enumeration has not quite finished. Opening the /devices
1243 * pathname will force the kernel to finish the enumeration process and
1244 * let us get the data we need.
1245 */
1246 static void
get_devid(di_node_t node,ddi_devid_t * thisdevid)1247 get_devid(di_node_t node, ddi_devid_t *thisdevid)
1248 {
1249 int fd;
1250 char realpath[MAXPATHLEN];
1251 char *openpath = di_devfs_path(node);
1252
1253 errno = 0;
1254 bzero(realpath, MAXPATHLEN);
1255 if (strstr(openpath, "/devices") == NULL) {
1256 (void) snprintf(realpath, MAXPATHLEN,
1257 "/devices%s:c,raw", openpath);
1258 fd = open(realpath, O_RDONLY|O_NDELAY);
1259 } else {
1260 fd = open(openpath, O_RDONLY|O_NDELAY);
1261 }
1262
1263 if (fd < 0) {
1264 logmsg(MSG_INFO, "Unable to open path %s: %s\n",
1265 openpath, strerror(errno));
1266 return;
1267 }
1268
1269 if (devid_get(fd, thisdevid) != 0) {
1270 logmsg(MSG_INFO,
1271 "'%s' node (%s) without a devid registered\n",
1272 di_driver_name(node), di_devfs_path(node));
1273 }
1274 (void) close(fd);
1275 }
1276
1277 static int
print_bootpath()1278 print_bootpath()
1279 {
1280 char *bootprop = NULL;
1281
1282 if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root,
1283 "bootpath", &bootprop) >= 0) {
1284 (void) printf("%s\n", bootprop);
1285 return (0);
1286 } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root,
1287 "boot-path", &bootprop) >= 0) {
1288 (void) printf("%s\n", bootprop);
1289 return (0);
1290 } else {
1291 (void) printf("ERROR: no bootpath/boot-path property found\n");
1292 return (ENOENT);
1293 }
1294 }
1295
1296 static void
get_phci_driver_name(char * phci_path,char ** driver_name)1297 get_phci_driver_name(char *phci_path, char **driver_name)
1298 {
1299 di_node_t phci_node = DI_NODE_NIL;
1300 char *tmp = NULL;
1301
1302 phci_node = di_init(phci_path, DINFOCPYONE);
1303 if (phci_node == DI_NODE_NIL) {
1304 logmsg(MSG_ERROR,
1305 gettext("Unable to take phci snapshot "
1306 "(%s: %d)\n"), strerror(errno), errno);
1307 return;
1308 }
1309 tmp = di_driver_name(phci_node);
1310 if (tmp != NULL) {
1311 (void) strncpy(*driver_name, tmp, 10);
1312 }
1313 di_fini(phci_node);
1314 }
1315
1316 /*
1317 * We only call this routine if we have a scsi_vhci node and must
1318 * determine the actual physical path of its first online client
1319 * path.
1320 */
1321 static void
vhci_to_phci(char * devpath,char * slice,int d_flag)1322 vhci_to_phci(char *devpath, char *slice, int d_flag)
1323 {
1324 sv_iocdata_t ioc;
1325 sv_path_info_t *pi;
1326 int vhci_fd;
1327 int rv;
1328 uint_t npaths = 0;
1329 char nodename[MAXPATHLEN];
1330 char *phci_driver = NULL;
1331
1332 vhci_fd = open(VHCI_CTL_NODE, O_RDWR);
1333 if (vhci_fd < 0)
1334 goto failure;
1335
1336 bzero(&ioc, sizeof (sv_iocdata_t));
1337 ioc.client = devpath;
1338 ioc.ret_elem = &npaths;
1339 rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc);
1340 if (rv || npaths == 0) {
1341 logmsg(MSG_INFO,
1342 "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() failed, "
1343 "%s (%d)\n", strerror(rv), rv);
1344 goto failure;
1345 }
1346
1347 bzero(&ioc, sizeof (sv_iocdata_t));
1348 ioc.client = devpath;
1349 ioc.buf_elem = npaths;
1350 ioc.ret_elem = &npaths;
1351 if ((ioc.ret_buf = calloc(npaths, sizeof (sv_path_info_t)))
1352 == NULL)
1353 goto failure;
1354 rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc);
1355 if (rv || npaths == 0) {
1356 logmsg(MSG_INFO,
1357 "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() (#2) "
1358 "failed, %s (%d)\n", strerror(rv), rv);
1359 free(ioc.ret_buf);
1360 goto failure;
1361 }
1362
1363 if (ioc.buf_elem < npaths)
1364 npaths = ioc.buf_elem;
1365
1366 phci_driver = malloc(10);
1367 if (phci_driver == NULL) {
1368 logmsg(MSG_INFO,
1369 "vhci_to_phci: Memory allocation failed\n");
1370 free(ioc.ret_buf);
1371 goto failure;
1372 }
1373
1374 pi = (sv_path_info_t *)ioc.ret_buf;
1375 while (npaths--) {
1376 bzero(nodename, MAXPATHLEN);
1377 bzero(phci_driver, 10);
1378
1379 get_phci_driver_name(pi->device.ret_phci,
1380 &phci_driver);
1381 logmsg(MSG_INFO, "phci driver name: %s\n", phci_driver);
1382 /*
1383 * A hack, but nicer than a platform-specific ifdef
1384 * fp on SPARC using "ssd" as nodename
1385 * mpt use "sd" when mpxio disabled, use "disk" when
1386 * mpxio is enabled
1387 * for alll other cases, "disk" should be used as the
1388 * nodename
1389 */
1390 if (strstr(devpath, "ssd") != NULL) {
1391 (void) snprintf(nodename, 5, "ssd");
1392 } else if (strncmp(phci_driver, "mpt", 10) == 0) {
1393 (void) snprintf(nodename, 5, "sd");
1394 } else {
1395 (void) snprintf(nodename, 5, "disk");
1396 }
1397 if ((d_flag == DISPLAY_ONE_PATH) &&
1398 (pi->ret_state == MDI_PATHINFO_STATE_ONLINE)) {
1399 (void) printf("%s/%s@%s", pi->device.ret_phci,
1400 nodename, pi->ret_addr);
1401 if ((slice != NULL) && (strlen(slice) <= 3)) {
1402 (void) printf("%s\n", slice);
1403 } else {
1404 (void) printf("\n");
1405 }
1406 break;
1407 } else if (d_flag == DISPLAY_ALL_PATH) {
1408 (void) printf("%s/%s@%s", pi->device.ret_phci,
1409 nodename, pi->ret_addr);
1410 if ((slice != NULL) && (strlen(slice) <= 3)) {
1411 (void) printf("%s\n", slice);
1412 } else {
1413 (void) printf("\n");
1414 }
1415 }
1416 pi++;
1417 }
1418 free(ioc.ret_buf);
1419 free(phci_driver);
1420 return;
1421
1422 failure:
1423 (void) printf("NOT_MAPPED\n");
1424 }
1425
1426 /*
1427 * Write /etc/vfstab to /etc/vfstab.new, with any remapped device
1428 * names substituted.
1429 *
1430 * Returns:
1431 * 0 successful operation
1432 * -1 failed
1433 */
1434 static int
update_vfstab()1435 update_vfstab()
1436 {
1437 FILE *fdin, *fdout;
1438 char *buf, *tmpbuf;
1439 char fname[MAXPATHLEN];
1440 int rv = -1, rval = -1;
1441 char cdev[MAXPATHLEN];
1442 char bdev[MAXPATHLEN];
1443 char mntpt[MAXPATHLEN];
1444 char fstype[512];
1445 char fsckpass[512];
1446 char mntboot[512];
1447 char mntopt[MAXPATHLEN];
1448 char fmt[80];
1449 char *prefixt = NULL;
1450 char *curdev = NULL;
1451 char *thisdevid = NULL;
1452 char *slice = NULL;
1453 nvlist_t *thisdev;
1454 boolean_t devmpx = B_FALSE;
1455
1456 buf = calloc(1, MAXPATHLEN);
1457 tmpbuf = calloc(1, MAXPATHLEN);
1458 if (buf == NULL || tmpbuf == NULL)
1459 return (-1);
1460
1461 (void) snprintf(fname, MAXPATHLEN, "/etc/mpxio/vfstab.new");
1462
1463 fdin = fopen("/etc/vfstab", "r");
1464 fdout = fopen(fname, "w+");
1465 if (fdin == NULL || fdout == NULL) {
1466 logmsg(MSG_INFO, "Unable to open vfstab or create a backup "
1467 "vfstab %s\n");
1468 return (-1);
1469 }
1470
1471 (void) snprintf(fmt, sizeof (fmt),
1472 "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1,
1473 sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1,
1474 sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1);
1475
1476 while (fgets(buf, MAXPATHLEN, fdin) != NULL) {
1477 if (strlen(buf) == (MAXPATHLEN - 1) &&
1478 buf[MAXPATHLEN-2] != '\n') {
1479 logmsg(MSG_ERROR,
1480 gettext("/etc/vfstab line length too long, "
1481 "exceeded %2$d: \"%3$s\"\n"),
1482 MAXPATHLEN - 2, buf);
1483 goto out;
1484 }
1485
1486 prefixt = NULL;
1487 curdev = NULL;
1488 slice = NULL;
1489 thisdevid = NULL;
1490 thisdev = NULL;
1491
1492 /* LINTED - variable format specifier */
1493 rv = sscanf(buf, fmt, bdev, cdev, mntpt, fstype, fsckpass,
1494 mntboot, mntopt);
1495
1496 /*
1497 * Walk through the lines in the input file (/etc/vfstab),
1498 * skipping anything which is _not_ a COGD (common or garden
1499 * disk), ie all the /devices, /system, /dev/md, /dev/vx and
1500 * /dev/zvol and so forth.
1501 */
1502 if ((rv == 7) && (bdev[0] == '/') &&
1503 (strstr(bdev, "/dev/dsk"))) {
1504 slice = strrchr(bdev, 's');
1505 /* take a copy, strip off /dev/dsk/ */
1506 prefixt = strrchr(bdev, 'c');
1507 prefixt[strlen(bdev) - 9 - strlen(slice)] = '\0';
1508 slice++; /* advance past the s */
1509 rval = nvlist_lookup_string(mapnvl, prefixt,
1510 &thisdevid);
1511 if (rval) {
1512 /* Whoa, where did this device go?! */
1513 logmsg(MSG_INFO,
1514 "error looking up device %s\n", prefixt);
1515 /* Comment-out this line in the new version */
1516 (void) snprintf(tmpbuf, MAXPATHLEN,
1517 "# DEVICE NOT FOUND %s", buf);
1518 (void) fprintf(fdout, "%s", tmpbuf);
1519 continue;
1520 } else {
1521 /* The device exists in our mapnvl */
1522 (void) nvlist_lookup_nvlist(mapnvl, thisdevid,
1523 &thisdev);
1524 (void) nvlist_lookup_boolean_value(thisdev,
1525 NVL_MPXEN, &devmpx);
1526 (void) nvlist_lookup_string(thisdev,
1527 ((devmpx == B_TRUE)
1528 ? NVL_MPXPATH : NVL_PATH),
1529 &curdev);
1530 }
1531 }
1532
1533 if ((prefixt != NULL) && (curdev != NULL) &&
1534 (rv = (strncmp(prefixt, curdev, strlen(prefixt)) != 0))) {
1535 /* Mapping change for this device */
1536 if (strcmp(fstype, "swap") == 0) {
1537 (void) snprintf(tmpbuf, MAXPATHLEN,
1538 "/dev/dsk/%ss%s\t-\t-\tswap\t"
1539 "%s\t%s\t%s\n",
1540 curdev, slice, fsckpass, mntboot, mntopt);
1541 } else {
1542 (void) snprintf(tmpbuf, MAXPATHLEN,
1543 "/dev/dsk/%ss%s\t/dev/rdsk/%ss%s\t"
1544 "%s\t%s\t%s\t%s\t%s\n",
1545 curdev, slice, curdev, slice,
1546 mntpt, fstype, fsckpass, mntboot, mntopt);
1547 }
1548 errno = 0;
1549 (void) fprintf(fdout, "%s", tmpbuf);
1550 } else {
1551 (void) fprintf(fdout, "%s", buf);
1552 }
1553
1554 errno = 0;
1555 if (fflush(fdout) != 0) {
1556 logmsg(MSG_ERROR,
1557 gettext("fprintf failed to write to %s: %s (%d)\n"),
1558 fname, strerror(errno), errno);
1559 goto out;
1560 }
1561 }
1562 out:
1563 (void) fclose(fdin);
1564 (void) fclose(fdout);
1565 free(buf);
1566 free(tmpbuf);
1567 return (errno);
1568 }
1569