xref: /illumos-gate/usr/src/lib/libdiskmgt/common/findevs.c (revision 12b7c4c541e123985df760270e5eaf96ab71324f)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24   * Use is subject to license terms.
25   */
26  
27  /*
28   * Copyright (c) 2011 by Delphix. All rights reserved.
29   * Copyright 2017 Nexenta Systems, Inc.
30   * Copyright 2021 Oxide Computer Company
31   * Copyright 2024 Sebastian Wiedenroth
32   */
33  
34  #include <fcntl.h>
35  #include <libdevinfo.h>
36  #include <stdio.h>
37  #include <stdlib.h>
38  #include <string.h>
39  #include <sys/stat.h>
40  #include <sys/sunddi.h>
41  #include <sys/types.h>
42  #include <sys/mkdev.h>
43  #include <ctype.h>
44  #include <libgen.h>
45  #include <unistd.h>
46  #include <devid.h>
47  #include <sys/fs/zfs.h>
48  
49  #include "libdiskmgt.h"
50  #include "disks_private.h"
51  
52  /* specify which disk links to use in the /dev directory */
53  #define	DEVLINK_REGEX		"rdsk/.*"
54  #define	DEVLINK_FLOPPY_REGEX	"rdiskette[0-9]"
55  
56  #define	FLOPPY_NAME	"rdiskette"
57  
58  #define	MAXPROPLEN		1024
59  #define	DEVICE_ID_PROP		"devid"
60  #define	INQUIRY_SERIAL_NO	"inquiry-serial-no"
61  #define	PROD_ID_PROP		"inquiry-product-id"
62  #define	PROD_ID_USB_PROP	"usb-product-name"
63  #define	REMOVABLE_PROP		"removable-media"
64  #define	HOTPLUGGABLE_PROP	"hotpluggable"
65  #define	SCSI_OPTIONS_PROP	"scsi-options"
66  #define	VENDOR_ID_PROP		"inquiry-vendor-id"
67  #define	VENDOR_ID_USB_PROP	"usb-vendor-name"
68  #define	WWN_PROP		"node-wwn"
69  
70  static char *ctrltypes[] = {
71  	DDI_NT_FC_ATTACHMENT_POINT,
72  	DDI_NT_NVME_ATTACHMENT_POINT,
73  	DDI_NT_SATA_ATTACHMENT_POINT,
74  	DDI_NT_SATA_NEXUS,
75  	DDI_NT_SCSI_ATTACHMENT_POINT,
76  	DDI_NT_SCSI_NEXUS,
77  	NULL
78  };
79  
80  static char *bustypes[] = {
81  	"sbus",
82  	"pci",
83  	"usb",
84  	NULL
85  };
86  
87  static bus_t		*add_bus(struct search_args *args, di_node_t node,
88  			    di_minor_t minor, controller_t *cp);
89  static controller_t	*add_controller(struct search_args *args,
90  			    di_node_t node, di_minor_t minor);
91  static int		add_devpath(di_devlink_t devlink, void *arg);
92  static int		add_devs(di_node_t node, di_minor_t minor, void *arg);
93  static int		add_disk2controller(disk_t *diskp,
94  			    struct search_args *args);
95  static int		add_disk2path(disk_t *dp, path_t *pp,
96  			    di_path_state_t st, char *wwn);
97  static int		add_int2array(int p, int **parray);
98  static int		add_ptr2array(void *p, void ***parray);
99  static char		*bus_type(di_node_t node, di_minor_t minor,
100  			    di_prom_handle_t ph);
101  static void		remove_controller(controller_t *cp,
102  			    controller_t *currp);
103  static void		clean_paths(struct search_args *args);
104  static disk_t		*create_disk(char *deviceid, char *kernel_name,
105  			    struct search_args *args);
106  static char		*ctype(di_node_t node, di_minor_t minor);
107  static boolean_t	disk_is_cdrom(const char *type);
108  static alias_t		*find_alias(disk_t *diskp, char *kernel_name);
109  static bus_t		*find_bus(struct search_args *args, char *name);
110  static controller_t	*find_controller(struct search_args *args, char *name);
111  static disk_t		*get_disk_by_deviceid(disk_t *listp, char *devid);
112  static void		get_disk_name_from_path(char *path, char *name,
113  			    int size);
114  static char		*get_byte_prop(char *prop_name, di_node_t node);
115  static di_node_t	get_parent_bus(di_node_t node,
116  			    struct search_args *args);
117  static int		get_prom_int(char *prop_name, di_node_t node,
118  			    di_prom_handle_t ph);
119  static char		*get_prom_str(char *prop_name, di_node_t node,
120  			    di_prom_handle_t ph);
121  static int		get_prop(char *prop_name, di_node_t node);
122  static char		*get_str_prop(char *prop_name, di_node_t node);
123  static int		have_disk(struct search_args *args, char *devid,
124  			    char *kernel_name, disk_t **diskp);
125  static int		is_ctds(char *name);
126  static int		is_drive(di_minor_t minor);
127  static int		is_zvol(di_node_t node, di_minor_t minor);
128  static int		is_ctrl(di_node_t node, di_minor_t minor);
129  static int		new_alias(disk_t *diskp, char *kernel_path,
130  			    char *devlink_path, struct search_args *args);
131  static int		new_devpath(alias_t *ap, char *devpath);
132  static path_t		*new_path(controller_t *cp, disk_t *diskp,
133  			    di_node_t node, di_path_state_t st, char *wwn);
134  static void		remove_invalid_controller(char *name,
135  			    controller_t *currp, struct search_args *args);
136  
137  /*
138   * The functions in this file do a dev tree walk to build up a model of the
139   * disks, controllers and paths on the system.  This model is returned in the
140   * args->disk_listp and args->controller_listp members of the args param.
141   * There is no global data for this file so it is thread safe.  It is up to
142   * the caller to merge the resulting model with any existing model that is
143   * cached.  The caller must also free the memory for this model when it is
144   * no longer needed.
145   */
146  void
findevs(struct search_args * args)147  findevs(struct search_args *args)
148  {
149  	di_node_t		di_root;
150  
151  	args->bus_listp = NULL;
152  	args->controller_listp = NULL;
153  	args->disk_listp = NULL;
154  
155  	args->ph = DI_PROM_HANDLE_NIL;
156  	args->handle = DI_LINK_NIL;
157  	args->dev_walk_status = 0;
158  
159  	/*
160  	 * Create device information library handles, which must be destroyed
161  	 * before we return.
162  	 */
163  	if ((args->ph = di_prom_init()) == DI_PROM_HANDLE_NIL ||
164  	    (args->handle = di_devlink_init(NULL, 0)) == DI_LINK_NIL) {
165  		/*
166  		 * We could not open all of the handles we need, so clean up
167  		 * and report failure to the caller.
168  		 */
169  		args->dev_walk_status = errno;
170  		goto cleanup;
171  	}
172  
173  	/*
174  	 * Have to make several passes at this with the new devfs caching.
175  	 * First, we find non-mpxio devices. Then we find mpxio/multipath
176  	 * devices.
177  	 */
178  	di_root = di_init("/", DINFOCACHE);
179  	(void) di_walk_minor(di_root, NULL, 0, args, add_devs);
180  	di_fini(di_root);
181  
182  	di_root = di_init("/", DINFOCPYALL|DINFOPATH);
183  	(void) di_walk_minor(di_root, NULL, 0, args, add_devs);
184  	di_fini(di_root);
185  
186  	clean_paths(args);
187  
188  cleanup:
189  	if (args->ph != DI_PROM_HANDLE_NIL) {
190  		di_prom_fini(args->ph);
191  		args->ph = DI_PROM_HANDLE_NIL;
192  	}
193  	if (args->handle != DI_LINK_NIL) {
194  		(void) di_devlink_fini(&(args->handle));
195  	}
196  }
197  
198  /*
199   * Definitions of private functions
200   */
201  
202  static bus_t *
add_bus(struct search_args * args,di_node_t node,di_minor_t minor,controller_t * cp)203  add_bus(struct search_args *args, di_node_t node, di_minor_t minor,
204      controller_t *cp)
205  {
206  	char		*btype;
207  	char		*devpath;
208  	bus_t		*bp;
209  	char		kstat_name[MAXPATHLEN];
210  	di_node_t	pnode;
211  
212  	if (node == DI_NODE_NIL) {
213  		return (NULL);
214  	}
215  
216  	if ((btype = bus_type(node, minor, args->ph)) == NULL) {
217  		return (add_bus(args, di_parent_node(node),
218  		    di_minor_next(di_parent_node(node), NULL), cp));
219  	}
220  
221  	devpath = di_devfs_path(node);
222  
223  	if ((bp = find_bus(args, devpath)) != NULL) {
224  		di_devfs_path_free((void *) devpath);
225  
226  		if (cp != NULL) {
227  			if (add_ptr2array(cp,
228  			    (void ***)&bp->controllers) != 0) {
229  				args->dev_walk_status = ENOMEM;
230  				return (NULL);
231  			}
232  		}
233  		return (bp);
234  	}
235  
236  	/* Special handling for root node. */
237  	if (strcmp(devpath, "/") == 0) {
238  		di_devfs_path_free((void *) devpath);
239  		return (NULL);
240  	}
241  
242  	if (dm_debug) {
243  		(void) fprintf(stderr, "INFO: add_bus %s\n", devpath);
244  	}
245  
246  	bp = (bus_t *)calloc(1, sizeof (bus_t));
247  	if (bp == NULL) {
248  		return (NULL);
249  	}
250  
251  	bp->name = strdup(devpath);
252  	di_devfs_path_free((void *) devpath);
253  	if (bp->name == NULL) {
254  		args->dev_walk_status = ENOMEM;
255  		cache_free_bus(bp);
256  		return (NULL);
257  	}
258  
259  	bp->btype = strdup(btype);
260  	if (bp->btype == NULL) {
261  		args->dev_walk_status = ENOMEM;
262  		cache_free_bus(bp);
263  		return (NULL);
264  	}
265  
266  	(void) snprintf(kstat_name, sizeof (kstat_name), "%s%d",
267  	    di_node_name(node), di_instance(node));
268  
269  	if ((bp->kstat_name = strdup(kstat_name)) == NULL) {
270  		args->dev_walk_status = ENOMEM;
271  		cache_free_bus(bp);
272  		return (NULL);
273  	}
274  
275  	/* if parent node is a bus, get its name */
276  	if ((pnode = get_parent_bus(node, args)) != NULL) {
277  		devpath = di_devfs_path(pnode);
278  		bp->pname = strdup(devpath);
279  		di_devfs_path_free((void *) devpath);
280  		if (bp->pname == NULL) {
281  			args->dev_walk_status = ENOMEM;
282  			cache_free_bus(bp);
283  			return (NULL);
284  		}
285  
286  	} else {
287  		bp->pname = NULL;
288  	}
289  
290  	bp->freq = get_prom_int("clock-frequency", node, args->ph);
291  
292  	bp->controllers = (controller_t **)calloc(1, sizeof (controller_t *));
293  	if (bp->controllers == NULL) {
294  		args->dev_walk_status = ENOMEM;
295  		cache_free_bus(bp);
296  		return (NULL);
297  	}
298  	bp->controllers[0] = NULL;
299  
300  	if (cp != NULL) {
301  		if (add_ptr2array(cp, (void ***)&bp->controllers) != 0) {
302  			args->dev_walk_status = ENOMEM;
303  			return (NULL);
304  		}
305  	}
306  
307  	bp->next = args->bus_listp;
308  	args->bus_listp = bp;
309  
310  	return (bp);
311  }
312  
313  static controller_t *
add_controller(struct search_args * args,di_node_t node,di_minor_t minor)314  add_controller(struct search_args *args, di_node_t node, di_minor_t minor)
315  {
316  	char		*devpath;
317  	controller_t	*cp;
318  	char		kstat_name[MAXPATHLEN];
319  	char		*c_type = DM_CTYPE_UNKNOWN;
320  
321  	devpath = di_devfs_path(node);
322  
323  	if ((cp = find_controller(args, devpath)) != NULL) {
324  		di_devfs_path_free((void *) devpath);
325  		return (cp);
326  	}
327  
328  	/* Special handling for fp attachment node. */
329  	if (strcmp(di_node_name(node), "fp") == 0) {
330  		di_node_t pnode;
331  
332  		pnode = di_parent_node(node);
333  		if (pnode != DI_NODE_NIL) {
334  			di_devfs_path_free((void *) devpath);
335  			devpath = di_devfs_path(pnode);
336  
337  			if ((cp = find_controller(args, devpath)) != NULL) {
338  				di_devfs_path_free((void *) devpath);
339  				return (cp);
340  			}
341  
342  			/* not in the list, create it */
343  			node = pnode;
344  			c_type = DM_CTYPE_FIBRE;
345  		}
346  	}
347  
348  	if (dm_debug) {
349  		(void) fprintf(stderr, "INFO: add_controller %s\n", devpath);
350  	}
351  
352  	cp = (controller_t *)calloc(1, sizeof (controller_t));
353  	if (cp == NULL) {
354  		return (NULL);
355  	}
356  
357  	cp->name = strdup(devpath);
358  	di_devfs_path_free((void *) devpath);
359  	if (cp->name == NULL) {
360  		cache_free_controller(cp);
361  		return (NULL);
362  	}
363  
364  	if (strcmp(c_type, DM_CTYPE_UNKNOWN) == 0) {
365  		c_type = ctype(node, minor);
366  	}
367  	cp->ctype = c_type;
368  
369  	(void) snprintf(kstat_name, sizeof (kstat_name), "%s%d",
370  	    di_node_name(node), di_instance(node));
371  
372  	if ((cp->kstat_name = strdup(kstat_name)) == NULL) {
373  		cache_free_controller(cp);
374  		return (NULL);
375  	}
376  
377  	if (libdiskmgt_str_eq(cp->ctype, "scsi")) {
378  		cp->scsi_options = get_prop(SCSI_OPTIONS_PROP, node);
379  	}
380  
381  	if (libdiskmgt_str_eq(di_node_name(node), "scsi_vhci")) {
382  		cp->multiplex = 1;
383  	} else {
384  		cp->multiplex = 0;
385  	}
386  
387  	cp->freq = get_prom_int("clock-frequency", node, args->ph);
388  
389  	cp->disks = (disk_t **)calloc(1, sizeof (disk_t *));
390  	if (cp->disks == NULL) {
391  		cache_free_controller(cp);
392  		return (NULL);
393  	}
394  	cp->disks[0] = NULL;
395  
396  	cp->next = args->controller_listp;
397  	args->controller_listp = cp;
398  
399  	cp->bus = add_bus(args, di_parent_node(node),
400  	    di_minor_next(di_parent_node(node), NULL), cp);
401  
402  	return (cp);
403  }
404  
405  static int
add_devpath(di_devlink_t devlink,void * arg)406  add_devpath(di_devlink_t devlink, void *arg)
407  {
408  	struct search_args *args;
409  	char		*devidstr;
410  	disk_t		*diskp;
411  	char		kernel_name[MAXPATHLEN];
412  
413  	args =	(struct search_args *)arg;
414  
415  	/*
416  	 * Get the diskp value from calling have_disk. Can either be found
417  	 * by kernel name or devid.
418  	 */
419  
420  	diskp = NULL;
421  	devidstr = get_str_prop(DEVICE_ID_PROP, args->node);
422  	(void) snprintf(kernel_name, sizeof (kernel_name), "%s%d",
423  	    di_node_name(args->node), di_instance(args->node));
424  
425  	(void) have_disk(args, devidstr, kernel_name, &diskp);
426  
427  	/*
428  	 * The devlink_path is usually of the form /dev/rdsk/c0t0d0s0.
429  	 * For diskettes it is /dev/rdiskette*.
430  	 * On Intel we would also get each fdisk partition as well
431  	 * (e.g. /dev/rdsk/c0t0d0p0).
432  	 */
433  	if (diskp != NULL) {
434  		alias_t	*ap;
435  		char	*devlink_path;
436  
437  		if (diskp->drv_type != DM_DT_FLOPPY) {
438  			/*
439  			 * Add other controllers for multipath disks.
440  			 * This will have no effect if the controller
441  			 * relationship is already set up.
442  			 */
443  			if (add_disk2controller(diskp, args) != 0) {
444  				args->dev_walk_status = ENOMEM;
445  			}
446  		}
447  
448  		(void) snprintf(kernel_name, sizeof (kernel_name), "%s%d",
449  		    di_node_name(args->node), di_instance(args->node));
450  		devlink_path = (char *)di_devlink_path(devlink);
451  
452  		if (dm_debug > 1) {
453  			(void) fprintf(stderr,
454  			    "INFO:     devpath %s\n", devlink_path);
455  		}
456  
457  		if ((ap = find_alias(diskp, kernel_name)) == NULL) {
458  			if (new_alias(diskp, kernel_name, devlink_path,
459  			    args) != 0) {
460  				args->dev_walk_status = ENOMEM;
461  			}
462  		} else {
463  			/*
464  			 * It is possible that we have already added this
465  			 * devpath.  Do not add it again. new_devpath will
466  			 * return a 0 if found, and not add the path.
467  			 */
468  			if (new_devpath(ap, devlink_path) != 0) {
469  				args->dev_walk_status = ENOMEM;
470  			}
471  		}
472  	}
473  
474  	return (DI_WALK_CONTINUE);
475  }
476  
477  static int
add_devs(di_node_t node,di_minor_t minor,void * arg)478  add_devs(di_node_t node, di_minor_t minor, void *arg)
479  {
480  	struct search_args	*args;
481  	int result = DI_WALK_CONTINUE;
482  
483  	args = (struct search_args *)arg;
484  
485  	if (dm_debug > 1) {
486  		/* This is all just debugging code */
487  		char	*devpath;
488  		char	dev_name[MAXPATHLEN];
489  
490  		devpath = di_devfs_path(node);
491  		(void) snprintf(dev_name, sizeof (dev_name), "%s:%s", devpath,
492  		    di_minor_name(minor));
493  		di_devfs_path_free((void *) devpath);
494  
495  		(void) fprintf(stderr,
496  		    "INFO: dev: %s, node: %s%d, minor: 0x%x, type: %s\n",
497  		    dev_name, di_node_name(node), di_instance(node),
498  		    di_minor_spectype(minor),
499  		    (di_minor_nodetype(minor) != NULL ?
500  		    di_minor_nodetype(minor) : "NULL"));
501  	}
502  
503  	if (bus_type(node, minor, args->ph) != NULL) {
504  		if (add_bus(args, node, minor, NULL) == NULL) {
505  			args->dev_walk_status = ENOMEM;
506  			result = DI_WALK_TERMINATE;
507  		}
508  
509  	} else if (is_ctrl(node, minor)) {
510  		if (add_controller(args, node, minor) == NULL) {
511  			args->dev_walk_status = ENOMEM;
512  			result = DI_WALK_TERMINATE;
513  		}
514  
515  	} else if (di_minor_spectype(minor) == S_IFCHR &&
516  	    (is_drive(minor) || is_zvol(node, minor))) {
517  		char	*devidstr;
518  		char	kernel_name[MAXPATHLEN];
519  		disk_t	*diskp;
520  
521  		(void) snprintf(kernel_name, sizeof (kernel_name), "%s%d",
522  		    di_node_name(node), di_instance(node));
523  		devidstr = get_str_prop(DEVICE_ID_PROP, node);
524  
525  		args->node = node;
526  		args->minor = minor;
527  		/*
528  		 * Check if we already got this disk and
529  		 * this is another slice.
530  		 */
531  		if (!have_disk(args, devidstr, kernel_name, &diskp)) {
532  			args->dev_walk_status = 0;
533  			/*
534  			 * This is a newly found disk, create the
535  			 * disk structure.
536  			 */
537  			diskp = create_disk(devidstr, kernel_name, args);
538  			if (diskp == NULL) {
539  				args->dev_walk_status = ENOMEM;
540  			}
541  
542  			if (diskp->drv_type != DM_DT_FLOPPY) {
543  				/* add the controller relationship */
544  				if (args->dev_walk_status == 0) {
545  					if (add_disk2controller(diskp,
546  					    args) != 0) {
547  						args->dev_walk_status = ENOMEM;
548  					}
549  				}
550  			}
551  		}
552  		if (is_zvol(node, minor)) {
553  			char zvdsk[MAXNAMELEN];
554  			char *str;
555  			alias_t *ap;
556  
557  			if (di_prop_lookup_strings(di_minor_devt(minor),
558  			    node, "name", &str) == -1)
559  				return (DI_WALK_CONTINUE);
560  			(void) snprintf(zvdsk, MAXNAMELEN, "/dev/zvol/rdsk/%s",
561  			    str);
562  			if ((ap = find_alias(diskp, kernel_name)) == NULL) {
563  				if (new_alias(diskp, kernel_name,
564  				    zvdsk, args) != 0) {
565  					args->dev_walk_status = ENOMEM;
566  				}
567  			} else {
568  				/*
569  				 * It is possible that we have already added
570  				 * this devpath.
571  				 * Do not add it again. new_devpath will
572  				 * return a 0 if found, and not add the path.
573  				 */
574  				if (new_devpath(ap, zvdsk) != 0) {
575  					args->dev_walk_status = ENOMEM;
576  				}
577  			}
578  		}
579  
580  		/* Add the devpaths for the drive. */
581  		if (args->dev_walk_status == 0) {
582  			char	*devpath;
583  			char	slice_path[MAXPATHLEN];
584  			char	*pattern;
585  
586  			/*
587  			 * We will come through here once for each of
588  			 * the raw slice device names.
589  			 */
590  			devpath = di_devfs_path(node);
591  			(void) snprintf(slice_path,
592  			    sizeof (slice_path), "%s:%s",
593  			    devpath, di_minor_name(minor));
594  			di_devfs_path_free((void *) devpath);
595  
596  			if (libdiskmgt_str_eq(di_minor_nodetype(minor),
597  			    DDI_NT_FD)) {
598  				pattern = DEVLINK_FLOPPY_REGEX;
599  			} else {
600  				pattern = DEVLINK_REGEX;
601  			}
602  
603  			/* Walk the /dev tree to get the devlinks. */
604  			(void) di_devlink_walk(args->handle, pattern,
605  			    slice_path, DI_PRIMARY_LINK, arg, add_devpath);
606  		}
607  
608  		if (args->dev_walk_status != 0) {
609  			result = DI_WALK_TERMINATE;
610  		}
611  	}
612  
613  	return (result);
614  }
615  
616  static int
add_disk2controller(disk_t * diskp,struct search_args * args)617  add_disk2controller(disk_t *diskp, struct search_args *args)
618  {
619  	di_node_t	pnode;
620  	controller_t	*cp;
621  	di_minor_t	minor;
622  	di_node_t	node;
623  	int		i;
624  
625  	node = args->node;
626  
627  	pnode = di_parent_node(node);
628  	if (pnode == DI_NODE_NIL) {
629  		return (0);
630  	}
631  
632  	/*
633  	 * Certain pseudo-device nodes do not all immediately have a valid
634  	 * parent-node. In particular, lofi (and zfs) would point to the generic
635  	 * /pseudo node. As a result, if we find a lofi disk, redirect it to the
636  	 * actual path. If we don't find it in this, then just fall back to the
637  	 * traditional path.
638  	 */
639  	if (libdiskmgt_str_eq(di_node_name(pnode), "pseudo") &&
640  	    libdiskmgt_str_eq(di_node_name(node), "lofi")) {
641  		di_node_t n;
642  
643  		n = di_drv_first_node("lofi", pnode);
644  		while (n != DI_NODE_NIL) {
645  			if (di_instance(n) == 0) {
646  				pnode = n;
647  				break;
648  			}
649  
650  			n = di_drv_next_node(n);
651  		}
652  	}
653  
654  	minor = di_minor_next(pnode, NULL);
655  	if (minor == NULL) {
656  		return (0);
657  	}
658  
659  	if ((cp = add_controller(args, pnode, minor)) == NULL) {
660  		return (ENOMEM);
661  	}
662  
663  	/* check if the disk <-> ctrl assoc is already there */
664  	for (i = 0; diskp->controllers[i]; i++) {
665  		if (cp == diskp->controllers[i]) {
666  			return (0);
667  		}
668  	}
669  
670  	/* this is a new controller for this disk */
671  
672  	/* add the disk to the controller */
673  	if (add_ptr2array(diskp, (void ***)&cp->disks) != 0) {
674  		return (ENOMEM);
675  	}
676  
677  	/* add the controller to the disk */
678  	if (add_ptr2array(cp, (void ***)&diskp->controllers) != 0) {
679  		return (ENOMEM);
680  	}
681  
682  	/*
683  	 * Set up paths for mpxio controlled drives.
684  	 */
685  	if (libdiskmgt_str_eq(di_node_name(pnode), "scsi_vhci")) {
686  		/* note: mpxio di_path stuff is all consolidation private */
687  		di_path_t   pi = DI_PATH_NIL;
688  
689  		while (
690  		    (pi = di_path_client_next_path(node, pi)) != DI_PATH_NIL) {
691  			int	cnt;
692  			uchar_t	*bytes;
693  			char	str[MAXPATHLEN];
694  			char	*wwn;
695  
696  			di_node_t phci_node = di_path_phci_node(pi);
697  
698  			/* get the node wwn */
699  			cnt = di_path_prop_lookup_bytes(pi, WWN_PROP, &bytes);
700  			wwn = NULL;
701  			if (cnt > 0) {
702  				int	i;
703  				str[0] = 0;
704  
705  				for (i = 0; i < cnt; i++) {
706  					/*
707  					 * A byte is only 2 hex chars + null.
708  					 */
709  					char bstr[8];
710  
711  					(void) snprintf(bstr,
712  					    sizeof (bstr), "%.2x", bytes[i]);
713  					(void) strlcat(str, bstr, sizeof (str));
714  				}
715  				wwn = str;
716  			}
717  
718  			if (new_path(cp, diskp, phci_node,
719  			    di_path_state(pi), wwn) == NULL) {
720  				return (ENOMEM);
721  			}
722  		}
723  	}
724  
725  	return (0);
726  }
727  
728  static int
add_disk2path(disk_t * dp,path_t * pp,di_path_state_t st,char * wwn)729  add_disk2path(disk_t *dp, path_t *pp, di_path_state_t st, char *wwn)
730  {
731  	/* add the disk to the path */
732  	if (add_ptr2array(dp, (void ***)&pp->disks) != 0) {
733  		cache_free_path(pp);
734  		return (0);
735  	}
736  
737  	/* add the path to the disk */
738  	if (add_ptr2array(pp, (void ***)&dp->paths) != 0) {
739  		cache_free_path(pp);
740  		return (0);
741  	}
742  
743  	/* add the path state for this disk */
744  	if (add_int2array(st, &pp->states) != 0) {
745  		cache_free_path(pp);
746  		return (0);
747  	}
748  
749  	/* add the path state for this disk */
750  	if (wwn != NULL) {
751  		char	*wp;
752  
753  		if ((wp = strdup(wwn)) != NULL) {
754  			if (add_ptr2array(wp, (void ***)(&pp->wwns)) != 0) {
755  				cache_free_path(pp);
756  				return (0);
757  			}
758  		}
759  	}
760  
761  	return (1);
762  }
763  
764  static int
add_int2array(int p,int ** parray)765  add_int2array(int p, int **parray)
766  {
767  	int		i;
768  	int		cnt;
769  	int		*pa;
770  	int		*new_array;
771  
772  	pa = *parray;
773  
774  	cnt = 0;
775  	if (pa != NULL) {
776  		for (; pa[cnt] != -1; cnt++)
777  			;
778  	}
779  
780  	new_array = (int *)calloc(cnt + 2, sizeof (int *));
781  	if (new_array == NULL) {
782  		return (ENOMEM);
783  	}
784  
785  	/* copy the existing array */
786  	for (i = 0; i < cnt; i++) {
787  		new_array[i] = pa[i];
788  	}
789  
790  	new_array[i] = p;
791  	new_array[i + 1] = -1;
792  
793  	free(pa);
794  	*parray = new_array;
795  
796  	return (0);
797  }
798  
799  static int
add_ptr2array(void * p,void *** parray)800  add_ptr2array(void *p, void ***parray)
801  {
802  	int		i;
803  	int		cnt;
804  	void		**pa;
805  	void		**new_array;
806  
807  	pa = *parray;
808  
809  	cnt = 0;
810  	if (pa != NULL) {
811  		for (; pa[cnt]; cnt++)
812  			;
813  	}
814  
815  	new_array = (void **)calloc(cnt + 2, sizeof (void *));
816  	if (new_array == NULL) {
817  		return (ENOMEM);
818  	}
819  
820  	/* copy the existing array */
821  	for (i = 0; i < cnt; i++) {
822  		new_array[i] = pa[i];
823  	}
824  
825  	new_array[i] = p;
826  	new_array[i + 1] = NULL;
827  
828  	free(pa);
829  	*parray = new_array;
830  
831  	return (0);
832  }
833  
834  /*
835   * This function checks to see if a controller has other associations
836   * that may be valid. If we are calling this function, we have found that
837   * a controller for an mpxio device is showing up independently of the
838   * mpxio controller, noted as /scsi_vhci. This can happen with some FC
839   * cards that have inbound management devices that show up as well, with
840   * the real controller data associated. We do not want to display these
841   * 'devices' as real devices in libdiskmgt.
842   */
843  static void
remove_controller(controller_t * cp,controller_t * currp)844  remove_controller(controller_t *cp, controller_t *currp)
845  {
846  	int	i;
847  
848  	if (cp == currp) {
849  		if (dm_debug) {
850  			(void) fprintf(stderr, "ERROR: removing current"
851  			    " controller\n");
852  		}
853  		return;
854  	}
855  
856  	if (cp->disks != NULL && cp->disks[0] != NULL) {
857  		if (dm_debug) {
858  			(void) fprintf(stderr,
859  			    "INFO: removing inbound management controller"
860  			    " with disk ptrs.\n");
861  		}
862  		/*
863  		 * loop through the disks and remove the reference to the
864  		 * controller for this disk structure. The disk itself
865  		 * is still a valid device, the controller being removed
866  		 * is a 'path' so any disk that has a reference to it
867  		 * as a controller needs to have this reference removed.
868  		 */
869  		for (i = 0; cp->disks[i]; i++) {
870  			disk_t *dp = cp->disks[i];
871  			int j;
872  
873  			for (j = 0; dp->controllers[j]; j++) {
874  				int k;
875  
876  				if (libdiskmgt_str_eq(dp->controllers[j]->name,
877  				    cp->name)) {
878  
879  					if (dm_debug) {
880  						(void) fprintf(stderr,
881  						    "INFO: REMOVING disk %s on "
882  						    "controller %s\n",
883  						    dp->kernel_name, cp->name);
884  					}
885  					for (k = j; dp->controllers[k]; k++) {
886  						dp->controllers[k] =
887  						    dp->controllers[k + 1];
888  					}
889  				}
890  			}
891  		}
892  	}
893  	/*
894  	 * Paths are removed with the call to cache_free_controller()
895  	 * below.
896  	 */
897  
898  	if (cp->paths != NULL && cp->paths[0] != NULL) {
899  		if (dm_debug) {
900  			(void) fprintf(stderr,
901  			    "INFO: removing inbound management controller"
902  			    " with path ptrs. \n");
903  		}
904  	}
905  	cache_free_controller(cp);
906  }
907  
908  /*
909   * If we have a controller in the list that is really a path then we need to
910   * take that controller out of the list since nodes that are paths are not
911   * considered to be controllers.
912   */
913  static void
clean_paths(struct search_args * args)914  clean_paths(struct search_args *args)
915  {
916  	controller_t	*cp;
917  
918  	cp = args->controller_listp;
919  	while (cp != NULL) {
920  		path_t	**pp;
921  
922  		pp = cp->paths;
923  		if (pp != NULL) {
924  			int i;
925  
926  			for (i = 0; pp[i]; i++) {
927  				remove_invalid_controller(pp[i]->name, cp,
928  				    args);
929  			}
930  		}
931  		cp = cp->next;
932  	}
933  }
934  
935  static disk_t *
create_disk(char * deviceid,char * kernel_name,struct search_args * args)936  create_disk(char *deviceid, char *kernel_name, struct search_args *args)
937  {
938  	disk_t	*diskp;
939  	char	*type;
940  	char	*prod_id;
941  	char	*vendor_id;
942  	char	*serial;
943  
944  	if (dm_debug) {
945  		(void) fprintf(stderr, "INFO: create_disk %s\n", kernel_name);
946  	}
947  
948  	diskp = calloc(1, sizeof (disk_t));
949  	if (diskp == NULL) {
950  		return (NULL);
951  	}
952  
953  	diskp->controllers = (controller_t **)
954  	    calloc(1, sizeof (controller_t *));
955  	if (diskp->controllers == NULL) {
956  		cache_free_disk(diskp);
957  		return (NULL);
958  	}
959  	diskp->controllers[0] = NULL;
960  
961  	diskp->devid = NULL;
962  	if (deviceid != NULL) {
963  		if ((diskp->device_id = strdup(deviceid)) == NULL) {
964  			cache_free_disk(diskp);
965  			return (NULL);
966  		}
967  		(void) devid_str_decode(deviceid, &(diskp->devid), NULL);
968  	}
969  
970  	if (kernel_name != NULL) {
971  		diskp->kernel_name = strdup(kernel_name);
972  		if (diskp->kernel_name == NULL) {
973  			cache_free_disk(diskp);
974  			return (NULL);
975  		}
976  	}
977  
978  	diskp->paths = NULL;
979  	diskp->aliases = NULL;
980  
981  	diskp->cd_rom = 0;
982  	diskp->rpm = 0;
983  	diskp->solid_state = -1;
984  	type = di_minor_nodetype(args->minor);
985  
986  	prod_id = get_str_prop(PROD_ID_PROP, args->node);
987  	if (prod_id != NULL) {
988  		if ((diskp->product_id = strdup(prod_id)) == NULL) {
989  			cache_free_disk(diskp);
990  			return (NULL);
991  		}
992  	} else {
993  		prod_id = get_str_prop(PROD_ID_USB_PROP, args->node);
994  		if (prod_id != NULL) {
995  			if ((diskp->product_id = strdup(prod_id)) == NULL) {
996  				cache_free_disk(diskp);
997  				return (NULL);
998  			}
999  		}
1000  	}
1001  
1002  	vendor_id = get_str_prop(VENDOR_ID_PROP, args->node);
1003  	if (vendor_id != NULL) {
1004  		if ((diskp->vendor_id = strdup(vendor_id)) == NULL) {
1005  			cache_free_disk(diskp);
1006  			return (NULL);
1007  		}
1008  	} else {
1009  		vendor_id = get_str_prop(VENDOR_ID_USB_PROP, args->node);
1010  		if (vendor_id != NULL) {
1011  			if ((diskp->vendor_id = strdup(vendor_id)) == NULL) {
1012  				cache_free_disk(diskp);
1013  				return (NULL);
1014  			}
1015  		}
1016  	}
1017  
1018  	serial = get_str_prop(INQUIRY_SERIAL_NO, args->node);
1019  	if (serial != NULL) {
1020  		if ((diskp->serial = strdup(serial)) == NULL) {
1021  			cache_free_disk(diskp);
1022  			return (NULL);
1023  		}
1024  	}
1025  
1026  	/*
1027  	 * DVD, CD-ROM, CD-RW, MO, etc. are all reported as CD-ROMS.
1028  	 * We try to use uscsi later to determine the real type.
1029  	 * The cd_rom flag tells us that the kernel categorized the drive
1030  	 * as a CD-ROM.  We leave the drv_type as UNKNOWN for now.
1031  	 * The combination of the cd_rom flag being set with the drv_type of
1032  	 * unknown is what triggers the uscsi probe in drive.c.
1033  	 */
1034  	if (disk_is_cdrom(type)) {
1035  		diskp->drv_type = DM_DT_UNKNOWN;
1036  		diskp->cd_rom = 1;
1037  		diskp->removable = 1;
1038  	} else if (libdiskmgt_str_eq(type, DDI_NT_FD)) {
1039  		diskp->drv_type = DM_DT_FLOPPY;
1040  		diskp->removable = 1;
1041  	} else {
1042  		/* not a CD-ROM or Floppy */
1043  		diskp->removable = get_prop(REMOVABLE_PROP, args->node);
1044  
1045  		if (diskp->removable == -1) {
1046  			diskp->removable = 0;
1047  			diskp->drv_type = DM_DT_FIXED;
1048  		}
1049  	}
1050  
1051  	diskp->next = args->disk_listp;
1052  	args->disk_listp = diskp;
1053  
1054  	return (diskp);
1055  }
1056  
1057  static char *
ctype(di_node_t node,di_minor_t minor)1058  ctype(di_node_t node, di_minor_t minor)
1059  {
1060  	char	*type;
1061  	char	*name;
1062  
1063  	type = di_minor_nodetype(minor);
1064  	name = di_node_name(node);
1065  
1066  	/* IDE disks use SCSI nexus as the type, so handle this special case */
1067  	if ((libdiskmgt_str_eq(type, DDI_NT_SCSI_NEXUS) ||
1068  	    libdiskmgt_str_eq(type, DDI_PSEUDO)) &&
1069  	    libdiskmgt_str_eq(name, "ide"))
1070  		return (DM_CTYPE_ATA);
1071  
1072  	if (libdiskmgt_str_eq(type, DDI_NT_FC_ATTACHMENT_POINT) ||
1073  	    (libdiskmgt_str_eq(type, DDI_NT_NEXUS) &&
1074  	    libdiskmgt_str_eq(name, "fp")))
1075  		return (DM_CTYPE_FIBRE);
1076  
1077  	if (libdiskmgt_str_eq(type, DDI_NT_NVME_ATTACHMENT_POINT))
1078  		return (DM_CTYPE_NVME);
1079  
1080  	if (libdiskmgt_str_eq(type, DDI_NT_SATA_NEXUS) ||
1081  	    libdiskmgt_str_eq(type, DDI_NT_SATA_ATTACHMENT_POINT))
1082  		return (DM_CTYPE_SATA);
1083  
1084  	if (libdiskmgt_str_eq(type, DDI_NT_SCSI_NEXUS) ||
1085  	    libdiskmgt_str_eq(type, DDI_NT_SCSI_ATTACHMENT_POINT))
1086  		return (DM_CTYPE_SCSI);
1087  
1088  	if (libdiskmgt_str_eq(di_minor_name(minor), "scsa2usb"))
1089  		return (DM_CTYPE_USB);
1090  
1091  	if (libdiskmgt_str_eq(type, DDI_PSEUDO) &&
1092  	    libdiskmgt_str_eq(name, "xpvd"))
1093  		return (DM_CTYPE_XEN);
1094  
1095  	if (libdiskmgt_str_eq(type, DDI_PSEUDO) &&
1096  	    libdiskmgt_str_eq(name, "lofi"))
1097  		return (DM_CTYPE_LOFI);
1098  
1099  	if (dm_debug) {
1100  		(void) fprintf(stderr,
1101  		    "INFO: unknown controller type=%s name=%s\n", type, name);
1102  	}
1103  
1104  	return (DM_CTYPE_UNKNOWN);
1105  }
1106  
1107  static boolean_t
disk_is_cdrom(const char * type)1108  disk_is_cdrom(const char *type)
1109  {
1110  	return (strncmp(type, DDI_NT_CD, strlen(DDI_NT_CD)) == 0);
1111  }
1112  
1113  static alias_t *
find_alias(disk_t * diskp,char * kernel_name)1114  find_alias(disk_t *diskp, char *kernel_name)
1115  {
1116  	alias_t	*ap;
1117  
1118  	ap = diskp->aliases;
1119  	while (ap != NULL) {
1120  		if (libdiskmgt_str_eq(ap->kstat_name, kernel_name)) {
1121  			return (ap);
1122  		}
1123  		ap = ap->next;
1124  	}
1125  
1126  	return (NULL);
1127  }
1128  
1129  static bus_t *
find_bus(struct search_args * args,char * name)1130  find_bus(struct search_args *args, char *name)
1131  {
1132  	bus_t *listp;
1133  
1134  	listp = args->bus_listp;
1135  	while (listp != NULL) {
1136  		if (libdiskmgt_str_eq(listp->name, name)) {
1137  			return (listp);
1138  		}
1139  		listp = listp->next;
1140  	}
1141  
1142  	return (NULL);
1143  }
1144  
1145  static controller_t *
find_controller(struct search_args * args,char * name)1146  find_controller(struct search_args *args, char *name)
1147  {
1148  	controller_t *listp;
1149  
1150  	listp = args->controller_listp;
1151  	while (listp != NULL) {
1152  		if (libdiskmgt_str_eq(listp->name, name)) {
1153  			return (listp);
1154  		}
1155  		listp = listp->next;
1156  	}
1157  
1158  	return (NULL);
1159  }
1160  
1161  /*
1162   * Check if we have the drive in our list, based upon the device id.
1163   * We got the device id from the dev tree walk.  This is encoded
1164   * using devid_str_encode(3DEVID).   In order to check the device ids we need
1165   * to use the devid_compare(3DEVID) function, so we need to decode the
1166   * string representation of the device id.
1167   */
1168  static disk_t *
get_disk_by_deviceid(disk_t * listp,char * devidstr)1169  get_disk_by_deviceid(disk_t *listp, char *devidstr)
1170  {
1171  	ddi_devid_t	devid;
1172  
1173  	if (devidstr == NULL || devid_str_decode(devidstr, &devid, NULL) != 0) {
1174  		return (NULL);
1175  	}
1176  
1177  	while (listp != NULL) {
1178  		if (listp->devid != NULL &&
1179  		    devid_compare(listp->devid, devid) == 0) {
1180  			break;
1181  		}
1182  		listp = listp->next;
1183  	}
1184  
1185  	devid_free(devid);
1186  	return (listp);
1187  }
1188  
1189  /*
1190   * Get the base disk name with no path prefix and no slice (if there is one).
1191   * The name parameter should be big enough to hold the name.
1192   * This handles diskette names ok (/dev/rdiskette0) since there is no slice,
1193   * and converts the raw diskette name.
1194   * But, we don't know how to strip off the slice from third party drive
1195   * names.  That just means that their drive name will include a slice on
1196   * it.
1197   */
1198  static void
get_disk_name_from_path(char * path,char * name,int size)1199  get_disk_name_from_path(char *path, char *name, int size)
1200  {
1201  	char		*basep;
1202  	int		cnt = 0;
1203  
1204  	basep = strrchr(path, '/');
1205  	if (basep == NULL) {
1206  		basep = path;
1207  	} else {
1208  		basep++;
1209  	}
1210  
1211  	size = size - 1;	/* leave room for terminating 0 */
1212  
1213  	if (is_ctds(basep)) {
1214  		while (*basep != 0 && *basep != 's' && cnt < size) {
1215  			*name++ = *basep++;
1216  				cnt++;
1217  		}
1218  		*name = 0;
1219  	} else {
1220  		if (strncmp(basep, FLOPPY_NAME,
1221  		    sizeof (FLOPPY_NAME) - 1) == 0) {
1222  			/*
1223  			 * a floppy, convert rdiskette name to diskette name,
1224  			 * by skipping over the 'r' for raw diskette
1225  			 */
1226  			basep++;
1227  		}
1228  
1229  		/* not a ctds name, just copy it */
1230  		(void) strlcpy(name, basep, size);
1231  	}
1232  }
1233  
1234  static char *
get_byte_prop(char * prop_name,di_node_t node)1235  get_byte_prop(char *prop_name, di_node_t node)
1236  {
1237  	int	cnt;
1238  	uchar_t	*bytes;
1239  	int	i;
1240  	char	str[MAXPATHLEN];
1241  
1242  	cnt = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, prop_name, &bytes);
1243  	if (cnt < 1) {
1244  		return (NULL);
1245  	}
1246  
1247  	str[0] = 0;
1248  	for (i = 0; i < cnt; i++) {
1249  		char bstr[8];	/* a byte is only 2 hex chars + null */
1250  
1251  		(void) snprintf(bstr, sizeof (bstr), "%.2x", bytes[i]);
1252  		(void) strlcat(str, bstr, sizeof (str));
1253  	}
1254  	return (strdup(str));
1255  }
1256  
1257  static di_node_t
get_parent_bus(di_node_t node,struct search_args * args)1258  get_parent_bus(di_node_t node, struct search_args *args)
1259  {
1260  	di_node_t pnode;
1261  
1262  	pnode = di_parent_node(node);
1263  	if (pnode == DI_NODE_NIL) {
1264  		return (NULL);
1265  	}
1266  
1267  	if (bus_type(pnode, di_minor_next(pnode, NULL), args->ph) != NULL) {
1268  		return (pnode);
1269  	}
1270  
1271  	return (get_parent_bus(pnode, args));
1272  }
1273  
1274  static int
get_prom_int(char * prop_name,di_node_t node,di_prom_handle_t ph)1275  get_prom_int(char *prop_name, di_node_t node, di_prom_handle_t ph)
1276  {
1277  	int *n;
1278  
1279  	if (di_prom_prop_lookup_ints(ph, node, prop_name, &n) == 1) {
1280  		return (*n);
1281  	}
1282  
1283  	return (0);
1284  }
1285  
1286  static char *
get_prom_str(char * prop_name,di_node_t node,di_prom_handle_t ph)1287  get_prom_str(char *prop_name, di_node_t node, di_prom_handle_t ph)
1288  {
1289  	char *str;
1290  
1291  	if (di_prom_prop_lookup_strings(ph, node, prop_name, &str) == 1) {
1292  		return (str);
1293  	}
1294  
1295  	return (NULL);
1296  }
1297  
1298  /*
1299   * Get one of the positive int or boolean properties.
1300   */
1301  static int
get_prop(char * prop_name,di_node_t node)1302  get_prop(char *prop_name, di_node_t node)
1303  {
1304  	int num;
1305  	int *ip;
1306  
1307  	if ((num = di_prop_lookup_ints(DDI_DEV_T_ANY, node, prop_name, &ip))
1308  	    >= 0) {
1309  		if (num == 0) {
1310  			/* boolean */
1311  			return (1);
1312  		} else if (num == 1) {
1313  			/* single int */
1314  			return (*ip);
1315  		}
1316  	}
1317  	return (-1);
1318  }
1319  
1320  static char *
get_str_prop(char * prop_name,di_node_t node)1321  get_str_prop(char *prop_name, di_node_t node)
1322  {
1323  	char *str;
1324  
1325  	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, prop_name, &str) == 1) {
1326  		return (str);
1327  	}
1328  
1329  	return (NULL);
1330  }
1331  
1332  /*
1333   * Check if we have the drive in our list, based upon the device id, if the
1334   * drive has a device id, or the kernel name, if it doesn't have a device id.
1335   */
1336  static int
have_disk(struct search_args * args,char * devidstr,char * kernel_name,disk_t ** diskp)1337  have_disk(struct search_args *args, char *devidstr, char *kernel_name,
1338      disk_t **diskp)
1339  {
1340  	disk_t *listp;
1341  
1342  	*diskp = NULL;
1343  	listp = args->disk_listp;
1344  	if (devidstr != NULL) {
1345  		if ((*diskp = get_disk_by_deviceid(listp, devidstr)) != NULL) {
1346  			return (1);
1347  		}
1348  
1349  	} else {
1350  		/* no devid, try matching the kernel names on the drives */
1351  		while (listp != NULL) {
1352  			if (libdiskmgt_str_eq(kernel_name,
1353  			    listp->kernel_name)) {
1354  				*diskp = listp;
1355  				return (1);
1356  			}
1357  			listp = listp->next;
1358  		}
1359  	}
1360  	return (0);
1361  }
1362  
1363  static char *
bus_type(di_node_t node,di_minor_t minor,di_prom_handle_t ph)1364  bus_type(di_node_t node, di_minor_t minor, di_prom_handle_t ph)
1365  {
1366  	char	*type;
1367  	int	i;
1368  
1369  	type = get_prom_str("device_type", node, ph);
1370  	if (type == NULL) {
1371  		type = di_node_name(node);
1372  	}
1373  
1374  	for (i = 0; bustypes[i]; i++) {
1375  		if (libdiskmgt_str_eq(type, bustypes[i])) {
1376  			return (type);
1377  		}
1378  	}
1379  
1380  	if (minor != NULL && strcmp(di_minor_nodetype(minor),
1381  	    DDI_NT_USB_ATTACHMENT_POINT) == 0) {
1382  		return ("usb");
1383  	}
1384  
1385  	return (NULL);
1386  }
1387  
1388  /*
1389   * If the input name is in c[t]ds format then return 1, otherwise return 0.
1390   */
1391  static int
is_ctds(char * name)1392  is_ctds(char *name)
1393  {
1394  	char	*p;
1395  
1396  	p = name;
1397  
1398  	if (*p++ != 'c') {
1399  		return (0);
1400  	}
1401  	/* skip controller digits */
1402  	while (isdigit(*p)) {
1403  		p++;
1404  	}
1405  
1406  	/* handle optional target */
1407  	if (*p == 't') {
1408  		p++;
1409  		/* skip over target */
1410  		while (isdigit(*p) || isupper(*p)) {
1411  			p++;
1412  		}
1413  	}
1414  
1415  	if (*p++ != 'd') {
1416  		return (0);
1417  	}
1418  	while (isdigit(*p)) {
1419  		p++;
1420  	}
1421  
1422  	if (*p++ != 's') {
1423  		return (0);
1424  	}
1425  
1426  	/* check the slice number */
1427  	while (isdigit(*p)) {
1428  		p++;
1429  	}
1430  
1431  	if (*p != 0) {
1432  		return (0);
1433  	}
1434  
1435  	return (1);
1436  }
1437  
1438  static int
is_drive(di_minor_t minor)1439  is_drive(di_minor_t minor)
1440  {
1441  	return (strncmp(di_minor_nodetype(minor), DDI_NT_BLOCK,
1442  	    strlen(DDI_NT_BLOCK)) == 0);
1443  }
1444  
1445  static int
is_zvol(di_node_t node,di_minor_t minor)1446  is_zvol(di_node_t node, di_minor_t minor)
1447  {
1448  	if ((strncmp(di_node_name(node), ZFS_DRIVER, 3) == 0) &&
1449  	    minor(di_minor_devt(minor)))
1450  		return (1);
1451  	return (0);
1452  }
1453  
1454  static int
is_ctrl(di_node_t node,di_minor_t minor)1455  is_ctrl(di_node_t node, di_minor_t minor)
1456  {
1457  	char	*type;
1458  	char	*name;
1459  	int	type_index;
1460  
1461  	type = di_minor_nodetype(minor);
1462  	type_index = 0;
1463  
1464  	while (ctrltypes[type_index] != NULL) {
1465  		if (libdiskmgt_str_eq(type, ctrltypes[type_index])) {
1466  			return (1);
1467  		}
1468  		type_index++;
1469  	}
1470  
1471  	name = di_node_name(node);
1472  	if (libdiskmgt_str_eq(type, DDI_PSEUDO) &&
1473  	    (libdiskmgt_str_eq(name, "ide") ||
1474  	    libdiskmgt_str_eq(name, "xpvd")))
1475  		return (1);
1476  
1477  	if (libdiskmgt_str_eq(type, DDI_PSEUDO) &&
1478  	    libdiskmgt_str_eq(name, "lofi") &&
1479  	    libdiskmgt_str_eq(di_minor_name(minor), "ctl"))
1480  		return (1);
1481  
1482  	return (0);
1483  }
1484  
1485  static int
new_alias(disk_t * diskp,char * kernel_name,char * devlink_path,struct search_args * args)1486  new_alias(disk_t *diskp, char *kernel_name, char *devlink_path,
1487      struct search_args *args)
1488  {
1489  	alias_t		*aliasp;
1490  	char		alias[MAXPATHLEN];
1491  	di_node_t	pnode;
1492  
1493  	aliasp = malloc(sizeof (alias_t));
1494  	if (aliasp == NULL) {
1495  		return (ENOMEM);
1496  	}
1497  
1498  	aliasp->alias = NULL;
1499  	aliasp->kstat_name = NULL;
1500  	aliasp->wwn = NULL;
1501  	aliasp->devpaths = NULL;
1502  	aliasp->orig_paths = NULL;
1503  
1504  	get_disk_name_from_path(devlink_path, alias, sizeof (alias));
1505  
1506  	aliasp->alias = strdup(alias);
1507  	if (aliasp->alias == NULL) {
1508  		cache_free_alias(aliasp);
1509  		return (ENOMEM);
1510  	}
1511  
1512  	if (kernel_name != NULL) {
1513  		aliasp->kstat_name = strdup(kernel_name);
1514  		if (aliasp->kstat_name == NULL) {
1515  			cache_free_alias(aliasp);
1516  			return (ENOMEM);
1517  		}
1518  	} else {
1519  		aliasp->kstat_name = NULL;
1520  	}
1521  
1522  	aliasp->lun = get_prop(DM_LUN, args->node);
1523  	aliasp->target = get_prop(DM_TARGET, args->node);
1524  	aliasp->wwn = get_byte_prop(WWN_PROP, args->node);
1525  
1526  	pnode = di_parent_node(args->node);
1527  	if (pnode != DI_NODE_NIL) {
1528  		char prop_name[MAXPROPLEN];
1529  
1530  		(void) snprintf(prop_name, sizeof (prop_name),
1531  		    "target%d-sync-speed", aliasp->target);
1532  		diskp->sync_speed = get_prop(prop_name, pnode);
1533  		(void) snprintf(prop_name, sizeof (prop_name), "target%d-wide",
1534  		    aliasp->target);
1535  		diskp->wide = get_prop(prop_name, pnode);
1536  	}
1537  
1538  	if (new_devpath(aliasp, devlink_path) != 0) {
1539  		cache_free_alias(aliasp);
1540  		return (ENOMEM);
1541  	}
1542  
1543  	aliasp->next = diskp->aliases;
1544  	diskp->aliases = aliasp;
1545  
1546  	return (0);
1547  }
1548  
1549  /*
1550   * Append the new devpath to the end of the devpath list.  This is important
1551   * since we may want to use the order of the devpaths to match up the vtoc
1552   * entries.
1553   */
1554  static int
new_devpath(alias_t * ap,char * devpath)1555  new_devpath(alias_t *ap, char *devpath)
1556  {
1557  	slice_t	*newdp;
1558  	slice_t *alistp;
1559  
1560  	/*
1561  	 * First, search the alias list to be sure that this devpath is
1562  	 * not already there.
1563  	 */
1564  
1565  	for (alistp = ap->devpaths; alistp != NULL; alistp = alistp->next) {
1566  		if (libdiskmgt_str_eq(alistp->devpath, devpath)) {
1567  			return (0);
1568  		}
1569  	}
1570  
1571  	/*
1572  	 * Otherwise, not found so add this new devpath to the list.
1573  	 */
1574  
1575  	newdp = malloc(sizeof (slice_t));
1576  	if (newdp == NULL) {
1577  		return (ENOMEM);
1578  	}
1579  
1580  	newdp->devpath = strdup(devpath);
1581  	if (newdp->devpath == NULL) {
1582  		free(newdp);
1583  		return (ENOMEM);
1584  	}
1585  	newdp->slice_num = -1;
1586  	newdp->next = NULL;
1587  
1588  	if (ap->devpaths == NULL) {
1589  		ap->devpaths = newdp;
1590  	} else {
1591  		/* append the devpath to the end of the list */
1592  		slice_t	*dp;
1593  
1594  		dp = ap->devpaths;
1595  		while (dp->next != NULL) {
1596  			dp = dp->next;
1597  		}
1598  
1599  		dp->next = newdp;
1600  	}
1601  
1602  	return (0);
1603  }
1604  
1605  static path_t *
new_path(controller_t * cp,disk_t * dp,di_node_t node,di_path_state_t st,char * wwn)1606  new_path(controller_t *cp, disk_t *dp, di_node_t node, di_path_state_t st,
1607      char *wwn)
1608  {
1609  	char		*devpath;
1610  	path_t		*pp;
1611  	di_minor_t	minor;
1612  
1613  	/* Special handling for fp attachment node. */
1614  	if (strcmp(di_node_name(node), "fp") == 0) {
1615  		di_node_t pnode;
1616  
1617  		pnode = di_parent_node(node);
1618  		if (pnode != DI_NODE_NIL) {
1619  			node = pnode;
1620  		}
1621  	}
1622  
1623  	devpath = di_devfs_path(node);
1624  
1625  	/* check if the path is already there */
1626  	pp = NULL;
1627  	if (cp->paths != NULL) {
1628  		int i;
1629  
1630  		for (i = 0; cp->paths[i]; i++) {
1631  			if (libdiskmgt_str_eq(devpath, cp->paths[i]->name)) {
1632  				pp = cp->paths[i];
1633  				break;
1634  			}
1635  		}
1636  	}
1637  
1638  	if (pp != NULL) {
1639  		/* the path exists, add this disk to it */
1640  
1641  		di_devfs_path_free((void *) devpath);
1642  		if (!add_disk2path(dp, pp, st, wwn)) {
1643  			return (NULL);
1644  		}
1645  		return (pp);
1646  	}
1647  
1648  	/* create a new path */
1649  
1650  	pp = calloc(1, sizeof (path_t));
1651  	if (pp == NULL) {
1652  		di_devfs_path_free((void *) devpath);
1653  		return (NULL);
1654  	}
1655  
1656  	pp->name = strdup(devpath);
1657  	di_devfs_path_free((void *) devpath);
1658  	if (pp->name == NULL) {
1659  		cache_free_path(pp);
1660  		return (NULL);
1661  	}
1662  
1663  	/* add the disk to the path */
1664  	if (!add_disk2path(dp, pp, st, wwn)) {
1665  		return (NULL);
1666  	}
1667  
1668  	/* add the path to the controller */
1669  	if (add_ptr2array(pp, (void ***)&cp->paths) != 0) {
1670  		cache_free_path(pp);
1671  		return (NULL);
1672  	}
1673  
1674  	/* add the controller to the path */
1675  	pp->controller = cp;
1676  
1677  	minor = di_minor_next(node, NULL);
1678  	if (minor != NULL) {
1679  		pp->ctype = ctype(node, minor);
1680  	} else {
1681  		pp->ctype = DM_CTYPE_UNKNOWN;
1682  	}
1683  
1684  	return (pp);
1685  }
1686  
1687  /*
1688   * We pass in the current controller pointer (currp) so we can double check
1689   * that we aren't corrupting the list by removing the element we are on.  This
1690   * should never happen, but it doesn't hurt to double check.
1691   */
1692  static void
remove_invalid_controller(char * name,controller_t * currp,struct search_args * args)1693  remove_invalid_controller(char *name, controller_t *currp,
1694      struct search_args *args)
1695  {
1696  	controller_t *cp;
1697  	bus_t *bp;
1698  	controller_t *prevp;
1699  
1700  	bp = args->bus_listp;
1701  	while (bp != NULL) {
1702  		int i;
1703  
1704  		for (i = 0; bp->controllers[i]; i++) {
1705  			if (libdiskmgt_str_eq(bp->controllers[i]->name, name)) {
1706  				int j;
1707  				/*
1708  				 * remove pointer to invalid controller.
1709  				 * (it is a path)
1710  				 */
1711  				for (j = i; bp->controllers[j]; j++) {
1712  					bp->controllers[j] =
1713  					    bp->controllers[j + 1];
1714  				}
1715  			}
1716  		}
1717  		bp = bp->next;
1718  	}
1719  
1720  	if (args->controller_listp == NULL) {
1721  		return;
1722  	}
1723  
1724  	cp = args->controller_listp;
1725  	if (libdiskmgt_str_eq(cp->name, name)) {
1726  		args->controller_listp = cp->next;
1727  		if (dm_debug) {
1728  			(void) fprintf(stderr,
1729  			    "INFO: Removed controller %s from list\n",
1730  			    cp->name);
1731  		}
1732  		remove_controller(cp, currp);
1733  		return;
1734  	}
1735  
1736  	prevp = cp;
1737  	cp = cp->next;
1738  	while (cp != NULL) {
1739  		if (libdiskmgt_str_eq(cp->name, name)) {
1740  			if (dm_debug) {
1741  				(void) fprintf(stderr,
1742  				    "INFO: Removed controller %s from list\n",
1743  				    cp->name);
1744  			}
1745  			prevp->next = cp->next;
1746  			remove_controller(cp, currp);
1747  			return;
1748  		}
1749  		prevp = cp;
1750  		cp = cp->next;
1751  	}
1752  }
1753