xref: /illumos-gate/usr/src/cmd/hal/hald/solaris/devinfo_storage.c (revision 42a7bded1b1244af097afdc88654381a3d3879f9)
1 /***************************************************************************
2  *
3  * devinfo_storage.c : storage devices
4  *
5  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
6  * Use is subject to license terms.
7  *
8  * Licensed under the Academic Free License version 2.1
9  *
10  **************************************************************************/
11 
12 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13 
14 #ifdef HAVE_CONFIG_H
15 #  include <config.h>
16 #endif
17 
18 #include <stdio.h>
19 #include <string.h>
20 #include <strings.h>
21 #include <ctype.h>
22 #include <libdevinfo.h>
23 #include <sys/types.h>
24 #include <sys/mkdev.h>
25 #include <sys/stat.h>
26 #include <sys/mntent.h>
27 #include <sys/mnttab.h>
28 
29 #include "../osspec.h"
30 #include "../logger.h"
31 #include "../hald.h"
32 #include "../hald_dbus.h"
33 #include "../device_info.h"
34 #include "../util.h"
35 #include "../hald_runner.h"
36 #include "hotplug.h"
37 #include "devinfo.h"
38 #include "devinfo_misc.h"
39 #include "devinfo_storage.h"
40 #include "osspec_solaris.h"
41 
42 #ifdef sparc
43 #define	WHOLE_DISK	"s2"
44 #else
45 #define	WHOLE_DISK	"p0"
46 #endif
47 
48 /* some devices,especially CDROMs, may take a while to be probed (values in ms) */
49 #define	DEVINFO_PROBE_STORAGE_TIMEOUT	60000
50 #define	DEVINFO_PROBE_VOLUME_TIMEOUT	60000
51 
52 typedef struct devinfo_storage_minor {
53 	char	*devpath;
54 	char	*devlink;
55 	char	*slice;
56 	dev_t	dev;
57 	int	dosnum;	/* dos disk number or -1 */
58 } devinfo_storage_minor_t;
59 
60 HalDevice *devinfo_ide_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
61 static HalDevice *devinfo_ide_host_add(HalDevice *parent, di_node_t node, char *devfs_path);
62 static HalDevice *devinfo_ide_device_add(HalDevice *parent, di_node_t node, char *devfs_path);
63 static HalDevice *devinfo_ide_storage_add(HalDevice *parent, di_node_t node, char *devfs_path);
64 HalDevice *devinfo_scsi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
65 static HalDevice *devinfo_scsi_storage_add(HalDevice *parent, di_node_t node, char *devfs_path);
66 HalDevice *devinfo_pcata_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
67 static HalDevice *devinfo_pcata_storage_add(HalDevice *parent, di_node_t node, char *devfs_path);
68 HalDevice *devinfo_floppy_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
69 static void devinfo_floppy_add_volume(HalDevice *parent, di_node_t node);
70 static HalDevice *devinfo_lofi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
71 static void devinfo_lofi_add_minor(HalDevice *parent, di_node_t node, char *minor_path, char *devlink, dev_t dev);
72 static void devinfo_storage_minors(HalDevice *parent, di_node_t node, gchar *devfs_path, gboolean);
73 static struct devinfo_storage_minor *devinfo_storage_new_minor(char *maindev_path, char *slice,
74     char *devlink, dev_t dev, int dosnum);
75 static void devinfo_storage_free_minor(struct devinfo_storage_minor *m);
76 HalDevice *devinfo_volume_add(HalDevice *parent, di_node_t node, devinfo_storage_minor_t *m);
77 static void devinfo_volume_preprobing_done(HalDevice *d, gpointer userdata1, gpointer userdata2);
78 static void devinfo_volume_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token);
79 static void devinfo_storage_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token);
80 static void devinfo_storage_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2);
81 const gchar *devinfo_volume_get_prober (HalDevice *d, int *timeout);
82 const gchar *devinfo_storage_get_prober (HalDevice *d, int *timeout);
83 
84 static char *devinfo_scsi_dtype2str(int dtype);
85 static char *devinfo_volume_get_slice_name (char *devlink);
86 static gboolean dos_to_dev(char *path, char **devpath, int *partnum);
87 static gboolean is_dos_path(char *path, int *partnum);
88 
89 static void devinfo_storage_set_nicknames (HalDevice *d);
90 
91 DevinfoDevHandler devinfo_ide_handler = {
92         devinfo_ide_add,
93 	NULL,
94 	NULL,
95 	NULL,
96 	NULL,
97         NULL
98 };
99 DevinfoDevHandler devinfo_scsi_handler = {
100         devinfo_scsi_add,
101 	NULL,
102 	NULL,
103 	NULL,
104 	NULL,
105         NULL
106 };
107 DevinfoDevHandler devinfo_pcata_handler = {
108         devinfo_pcata_add,
109 	NULL,
110 	NULL,
111 	NULL,
112 	NULL,
113         NULL
114 };
115 DevinfoDevHandler devinfo_floppy_handler = {
116         devinfo_floppy_add,
117 	NULL,
118 	NULL,
119 	NULL,
120 	NULL,
121         NULL
122 };
123 DevinfoDevHandler devinfo_lofi_handler = {
124         devinfo_lofi_add,
125 	NULL,
126 	NULL,
127 	NULL,
128 	NULL,
129         NULL
130 };
131 DevinfoDevHandler devinfo_storage_handler = {
132 	NULL,
133 	NULL,
134 	devinfo_storage_hotplug_begin_add,
135 	NULL,
136 	devinfo_storage_probing_done,
137 	devinfo_storage_get_prober
138 };
139 DevinfoDevHandler devinfo_volume_handler = {
140 	NULL,
141 	NULL,
142 	devinfo_volume_hotplug_begin_add,
143 	NULL,
144 	NULL,
145 	devinfo_volume_get_prober
146 };
147 
148 /* IDE */
149 
150 HalDevice *
151 devinfo_ide_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
152 {
153 	char	*s;
154 
155 	if ((device_type != NULL) && (strcmp(device_type, "ide") == 0)) {
156 		return (devinfo_ide_host_add(parent, node, devfs_path));
157 	}
158 
159         if ((di_prop_lookup_strings (DDI_DEV_T_ANY, node, "class", &s) > 0) &&
160 	    (strcmp (s, "dada") == 0)) {
161 		return (devinfo_ide_device_add(parent, node, devfs_path));
162 	}
163 
164 	return (NULL);
165 }
166 
167 static HalDevice *
168 devinfo_ide_host_add(HalDevice *parent, di_node_t node, char *devfs_path)
169 {
170 	HalDevice *d;
171 
172 	d = hal_device_new ();
173 
174 	devinfo_set_default_properties (d, parent, node, devfs_path);
175 	hal_device_property_set_string (d, "info.product", "IDE host controller");
176 	hal_device_property_set_string (d, "info.bus", "ide_host");
177 	hal_device_property_set_int (d, "ide_host.number", 0); /* XXX */
178 
179 	devinfo_add_enqueue (d, devfs_path, &devinfo_ide_handler);
180 
181 	return (d);
182 }
183 
184 static HalDevice *
185 devinfo_ide_device_add(HalDevice *parent, di_node_t node, char *devfs_path)
186 {
187 	HalDevice *d;
188 
189 	d = hal_device_new();
190 
191 	devinfo_set_default_properties (d, parent, node, devfs_path);
192         hal_device_property_set_string (parent, "info.product", "IDE device");
193 	hal_device_property_set_string (parent, "info.bus", "ide");
194 	hal_device_property_set_int (parent, "ide.host", 0); /* XXX */
195 	hal_device_property_set_int (parent, "ide.channel", 0);
196 
197 	devinfo_add_enqueue (d, devfs_path, &devinfo_ide_handler);
198 
199 	return (devinfo_ide_storage_add (d, node, devfs_path));
200 }
201 
202 static HalDevice *
203 devinfo_ide_storage_add(HalDevice *parent, di_node_t node, char *devfs_path)
204 {
205 	HalDevice *d;
206 	char	*s;
207 	int	*i;
208 	char	*driver_name;
209 	char	udi[HAL_PATH_MAX];
210 
211 	if ((driver_name = di_driver_name (node)) == NULL) {
212 		return (NULL);
213 	}
214 
215         d = hal_device_new ();
216 
217 	devinfo_set_default_properties (d, parent, node, devfs_path);
218         hal_device_property_set_string (d, "info.category", "storage");
219 
220         hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
221                 "%s/%s%d", hal_device_get_udi (parent), driver_name, di_instance (node));
222         hal_device_set_udi (d, udi);
223         hal_device_property_set_string (d, "info.udi", udi);
224 	PROP_STR(d, node, s, "devid", "info.product");
225 
226         hal_device_add_capability (d, "storage");
227         hal_device_property_set_string (d, "storage.bus", "ide");
228         hal_device_property_set_int (d, "storage.lun", 0);
229 	hal_device_property_set_string (d, "storage.drive_type", "disk");
230 
231 	PROP_BOOL(d, node, i, "hotpluggable", "storage.hotpluggable");
232 	PROP_BOOL(d, node, i, "removable-media", "storage.removable");
233 
234         hal_device_property_set_bool (d, "storage.media_check_enabled", FALSE);
235 
236 	/* XXX */
237         hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
238 
239 	hal_device_add_capability (d, "block");
240 
241 	devinfo_storage_minors (d, node, (char *)devfs_path, FALSE);
242 
243 	return (d);
244 }
245 
246 /* SCSI */
247 
248 HalDevice *
249 devinfo_scsi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
250 {
251 	int	*i;
252 	char	*driver_name;
253 	HalDevice *d;
254 	char	udi[HAL_PATH_MAX];
255 
256 	driver_name = di_driver_name (node);
257 	if ((driver_name == NULL) || (strcmp (driver_name, "sd") != 0)) {
258 		return (NULL);
259 	}
260 
261 	d = hal_device_new ();
262 
263 	devinfo_set_default_properties (d, parent, node, devfs_path);
264 	hal_device_property_set_string (d, "info.bus", "scsi");
265 
266         hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
267                 "%s/%s%d", hal_device_get_udi (parent), di_node_name(node), di_instance (node));
268         hal_device_set_udi (d, udi);
269         hal_device_property_set_string (d, "info.udi", udi);
270 
271 	hal_device_property_set_int (d, "scsi.host",
272 		hal_device_property_get_int (parent, "scsi_host.host"));
273 	hal_device_property_set_int (d, "scsi.bus", 0);
274 	PROP_INT(d, node, i, "target", "scsi.target");
275 	PROP_INT(d, node, i, "lun", "scsi.lun");
276         hal_device_property_set_string (d, "info.product", "SCSI Device");
277 
278         devinfo_add_enqueue (d, devfs_path, &devinfo_scsi_handler);
279 
280         return (devinfo_scsi_storage_add (d, node, devfs_path));
281 }
282 
283 static HalDevice *
284 devinfo_scsi_storage_add(HalDevice *parent, di_node_t node, char *devfs_path)
285 {
286 	HalDevice *d;
287 	int	*i;
288 	char	*s;
289 	char	udi[HAL_PATH_MAX];
290 
291 	d = hal_device_new ();
292 
293 	devinfo_set_default_properties (d, parent, node, devfs_path);
294         hal_device_property_set_string (d, "info.category", "storage");
295 
296         hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
297 		"%s/sd%d", hal_device_get_udi (parent), di_instance (node));
298         hal_device_set_udi (d, udi);
299         hal_device_property_set_string (d, "info.udi", udi);
300 	PROP_STR(d, node, s, "inquiry-product-id", "info.product");
301 
302         hal_device_add_capability (d, "storage");
303 
304         hal_device_property_set_int (d, "storage.lun",
305 		hal_device_property_get_int (parent, "scsi.lun"));
306 	PROP_BOOL(d, node, i, "hotpluggable", "storage.hotpluggable");
307 	PROP_BOOL(d, node, i, "removable-media", "storage.removable");
308         hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
309 
310 	/*
311 	 * We have to enable polling not only for drives with removable media,
312 	 * but also for hotpluggable devices, because when a disk is
313 	 * unplugged while busy/mounted, there is not sysevent generated.
314 	 * Instead, the HBA driver (scsa2usb, scsa1394) will notify sd driver
315 	 * and the latter will report DKIO_DEV_GONE via DKIOCSTATE ioctl.
316 	 * So we have to enable media check so that hald-addon-storage notices
317 	 * the "device gone" condition and unmounts all associated volumes.
318 	 */
319 	hal_device_property_set_bool (d, "storage.media_check_enabled",
320 	    ((di_prop_lookup_ints(DDI_DEV_T_ANY, node, "removable-media", &i) >= 0) ||
321 	    (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "hotpluggable", &i) >= 0)));
322 
323         if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
324 	    &i) > 0) {
325 		s = devinfo_scsi_dtype2str (*i);
326         	hal_device_property_set_string (d, "storage.drive_type", s);
327 
328 		if (strcmp (s, "cdrom") == 0) {
329 			hal_device_add_capability (d, "storage.cdrom");
330 			hal_device_property_set_bool (d, "storage.no_partitions_hint", TRUE);
331         		hal_device_property_set_bool (d, "storage.requires_eject", TRUE);
332 		}
333 	}
334 
335         hal_device_add_capability (d, "block");
336 
337 	devinfo_storage_minors (d, node, devfs_path, FALSE);
338 
339 	return (d);
340 }
341 
342 static char *
343 devinfo_scsi_dtype2str(int dtype)
344 {
345         char *dtype2str[] = {
346                 "disk"	,         /* DTYPE_DIRECT         0x00 */
347                 "tape"	,         /* DTYPE_SEQUENTIAL     0x01 */
348                 "printer",         /* DTYPE_PRINTER        0x02 */
349                 "processor",         /* DTYPE_PROCESSOR      0x03 */
350                 "worm"	,         /* DTYPE_WORM           0x04 */
351                 "cdrom"	,         /* DTYPE_RODIRECT       0x05 */
352                 "scanner",         /* DTYPE_SCANNER        0x06 */
353                 "cdrom"	,         /* DTYPE_OPTICAL        0x07 */
354                 "changer",         /* DTYPE_CHANGER        0x08 */
355                 "comm"	,         /* DTYPE_COMM           0x09 */
356                 "scsi"	,         /* DTYPE_???            0x0A */
357                 "scsi"	,         /* DTYPE_???            0x0B */
358                 "array_ctrl",         /* DTYPE_ARRAY_CTRL     0x0C */
359                 "esi"	,         /* DTYPE_ESI            0x0D */
360                 "disk"	          /* DTYPE_RBC            0x0E */
361         };
362 
363         if (dtype < NELEM(dtype2str)) {
364                 return (dtype2str[dtype]);
365         } else {
366 		return ("scsi");
367         }
368 
369 }
370 
371 /* PCMCIA */
372 
373 HalDevice *
374 devinfo_pcata_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
375 {
376 	int	*i;
377 	char	*driver_name;
378 	HalDevice *d;
379 	char	udi[HAL_PATH_MAX];
380 
381 	driver_name = di_driver_name (node);
382 	if ((driver_name == NULL) || (strcmp (driver_name, "pcata") != 0)) {
383 		return (NULL);
384 	}
385 
386 	d = hal_device_new ();
387 
388 	devinfo_set_default_properties (d, parent, node, devfs_path);
389 	hal_device_property_set_string (d, "info.bus", "pcmcia");
390 
391         hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
392                 "%s/%s%d", hal_device_get_udi (parent), di_node_name(node), di_instance (node));
393         hal_device_set_udi (d, udi);
394         hal_device_property_set_string (d, "info.udi", udi);
395         hal_device_property_set_string (d, "info.product", "PCMCIA Disk");
396 
397         devinfo_add_enqueue (d, devfs_path, &devinfo_pcata_handler);
398 
399         return (devinfo_pcata_storage_add (d, node, devfs_path));
400 }
401 
402 static HalDevice *
403 devinfo_pcata_storage_add(HalDevice *parent, di_node_t node, char *devfs_path)
404 {
405 	HalDevice *d;
406 	char	*driver_name;
407 	int	*i;
408 	char	*s;
409 	char	udi[HAL_PATH_MAX];
410 
411 	d = hal_device_new ();
412 
413 	devinfo_set_default_properties (d, parent, node, devfs_path);
414 	hal_device_property_set_string (d, "info.category", "storage");
415 
416 	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
417 		"%s/sd%d", hal_device_get_udi (parent), di_instance (node));
418 	hal_device_set_udi (d, udi);
419 	hal_device_property_set_string (d, "info.udi", udi);
420 
421 	hal_device_add_capability (d, "storage");
422 
423 	hal_device_property_set_int (d, "storage.lun", 0);
424 	hal_device_property_set_bool (d, "storage.hotpluggable", TRUE);
425 	hal_device_property_set_bool (d, "storage.removable", FALSE);
426 	hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
427 	hal_device_property_set_bool (d, "storage.media_check_enabled", TRUE);
428        	hal_device_property_set_string (d, "storage.drive_type", "disk");
429 	hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
430 
431 	hal_device_add_capability (d, "block");
432 
433 	devinfo_storage_minors (d, node, devfs_path, FALSE);
434 
435 	return (d);
436 }
437 
438 /* floppy */
439 
440 HalDevice *
441 devinfo_floppy_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
442 {
443 	char	*driver_name;
444 	char	*raw;
445 	char	udi[HAL_PATH_MAX];
446 	di_devlink_handle_t devlink_hdl;
447         int     major;
448         di_minor_t minor;
449         dev_t   dev;
450 	HalDevice *d = NULL;
451         char    *minor_path = NULL;
452 	char	*devlink = NULL;
453 
454 	driver_name = di_driver_name (node);
455 	if ((driver_name == NULL) || (strcmp (driver_name, "fd") != 0)) {
456 		return (NULL);
457 	}
458 
459 	/*
460 	 * The only minor node we're interested in is /dev/diskette*
461 	 */
462 	major = di_driver_major(node);
463 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
464 		return (NULL);
465 	}
466 	minor = DI_MINOR_NIL;
467 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
468 		dev = di_minor_devt(minor);
469 		if ((major != major(dev)) ||
470 		    (di_minor_type(minor) != DDM_MINOR) ||
471 		    (di_minor_spectype(minor) != S_IFBLK) ||
472 		    ((minor_path = di_devfs_minor_path(minor)) == NULL)) {
473 			continue;
474 		}
475 		if ((devlink = get_devlink(devlink_hdl, "diskette.+" , minor_path)) != NULL) {
476 			break;
477 		}
478 		di_devfs_path_free (minor_path);
479 		minor_path = NULL;
480 		free(devlink);
481 		devlink = NULL;
482 	}
483 	di_devlink_fini (&devlink_hdl);
484 
485 	if ((devlink == NULL) || (minor_path == NULL)) {
486 		HAL_INFO (("floppy devlink not found %s", devfs_path));
487 		goto out;
488 	}
489 
490 	d = hal_device_new ();
491 
492 	devinfo_set_default_properties (d, parent, node, devfs_path);
493         hal_device_property_set_string (d, "info.category", "storage");
494         hal_device_add_capability (d, "storage");
495        	hal_device_property_set_string (d, "storage.bus", "platform");
496         hal_device_property_set_bool (d, "storage.hotpluggable", FALSE);
497         hal_device_property_set_bool (d, "storage.removable", TRUE);
498         hal_device_property_set_bool (d, "storage.requires_eject", TRUE);
499         hal_device_property_set_bool (d, "storage.media_check_enabled", FALSE);
500        	hal_device_property_set_string (d, "storage.drive_type", "floppy");
501 
502         hal_device_add_capability (d, "block");
503 	hal_device_property_set_bool (d, "block.is_volume", FALSE);
504 	hal_device_property_set_int (d, "block.major", major(dev));
505 	hal_device_property_set_int (d, "block.minor", minor(dev));
506 	hal_device_property_set_string (d, "block.device", devlink);
507 	raw = dsk_to_rdsk (devlink);
508 	hal_device_property_set_string (d, "block.solaris.raw_device", raw);
509 	free (raw);
510 
511 	devinfo_add_enqueue (d, devfs_path, &devinfo_storage_handler);
512 
513 	/* trigger initial probe-volume */
514 	devinfo_floppy_add_volume(d, node);
515 
516 out:
517 	di_devfs_path_free (minor_path);
518 	free(devlink);
519 
520 	return (d);
521 }
522 
523 static void
524 devinfo_floppy_add_volume(HalDevice *parent, di_node_t node)
525 {
526 	char	*devlink;
527 	char	*devfs_path;
528 	int	minor, major;
529 	dev_t	dev;
530 	struct devinfo_storage_minor *m;
531 
532 	devfs_path = (char *)hal_device_property_get_string (parent, "solaris.devfs_path");
533 	devlink = (char *)hal_device_property_get_string (parent, "block.device");
534 	major = hal_device_property_get_int (parent, "block.major");
535 	minor = hal_device_property_get_int (parent, "block.minor");
536 	dev = makedev (major, minor);
537 
538 	m = devinfo_storage_new_minor (devfs_path, WHOLE_DISK, devlink, dev, -1);
539 	devinfo_volume_add (parent, node, m);
540 	devinfo_storage_free_minor (m);
541 }
542 
543 /*
544  * After reprobing storage, reprobe its volumes.
545  */
546 static void
547 devinfo_floppy_rescan_probing_done (HalDevice *d, guint32 exit_type, gint return_code,
548     char **error, gpointer userdata1, gpointer userdata2)
549 {
550         void *end_token = (void *) userdata1;
551 	const char *devfs_path;
552 	di_node_t node;
553 	HalDevice *v;
554 
555 	if (!hal_device_property_get_bool (d, "storage.removable.media_available")) {
556 		HAL_INFO (("no floppy media", hal_device_get_udi (d)));
557 
558 		/* remove child (can only be single volume) */
559 		if (((v = hal_device_store_match_key_value_string (hald_get_gdl(),
560         	    "info.parent", hal_device_get_udi (d))) != NULL) &&
561 		    ((devfs_path = hal_device_property_get_string (v,
562 		    "solaris.devfs_path")) != NULL)) {
563 			devinfo_remove_enqueue ((char *)devfs_path, NULL);
564 		}
565 	} else {
566 		HAL_INFO (("floppy media found", hal_device_get_udi (d)));
567 
568 		if ((devfs_path = hal_device_property_get_string(d, "solaris.devfs_path")) == NULL) {
569 			HAL_INFO (("no devfs_path", hal_device_get_udi (d)));
570 			hotplug_event_process_queue ();
571 			return;
572 		}
573 		if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
574 			HAL_INFO (("di_init %s failed %d", devfs_path, errno));
575 			hotplug_event_process_queue ();
576 			return;
577 		}
578 
579 		devinfo_floppy_add_volume (d, node);
580 
581 		di_fini (node);
582 	}
583 
584 	hotplug_event_process_queue ();
585 }
586 
587 /* lofi */
588 
589 HalDevice *
590 devinfo_lofi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
591 {
592 	return (devinfo_lofi_add_major(parent,node, devfs_path, device_type, FALSE, NULL));
593 }
594 
595 HalDevice *
596 devinfo_lofi_add_major(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type,
597     gboolean rescan, HalDevice *lofi_d)
598 {
599 	char	*driver_name;
600 	HalDevice *d = NULL;
601 	char	udi[HAL_PATH_MAX];
602 	di_devlink_handle_t devlink_hdl;
603         int     major;
604         di_minor_t minor;
605         dev_t   dev;
606         char    *minor_path = NULL;
607         char    *devpath, *devlink;
608 
609 	driver_name = di_driver_name (node);
610 	if ((driver_name == NULL) || (strcmp (driver_name, "lofi") != 0)) {
611 		return (NULL);
612 	}
613 
614 	if (!rescan) {
615 		d = hal_device_new ();
616 
617 		devinfo_set_default_properties (d, parent, node, devfs_path);
618 		hal_device_property_set_string (d, "info.bus", "pseudo");
619 
620         	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
621                 	"%s/%s%d", hal_device_get_udi (parent), di_node_name(node), di_instance (node));
622         	hal_device_set_udi (d, udi);
623         	hal_device_property_set_string (d, "info.udi", udi);
624 
625         	devinfo_add_enqueue (d, devfs_path, &devinfo_lofi_handler);
626 	} else {
627 		d = lofi_d;
628 	}
629 
630 	/*
631 	 * Unlike normal storage, as in devinfo_storage_minors(), where
632 	 * sd instance -> HAL storage, sd minor node -> HAL volume,
633 	 * lofi always has one instance, lofi minor -> HAL storage.
634 	 * lofi storage never has slices, but it can have
635 	 * embedded pcfs partitions that fstyp would recognize
636 	 */
637 	major = di_driver_major(node);
638 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
639 		return (d);
640 	}
641 	minor = DI_MINOR_NIL;
642 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
643 		dev = di_minor_devt(minor);
644 		if ((major != major(dev)) ||
645 		    (di_minor_type(minor) != DDM_MINOR) ||
646 		    (di_minor_spectype(minor) != S_IFBLK) ||
647 		    ((minor_path = di_devfs_minor_path(minor)) == NULL)) {
648 			continue;
649 		}
650 		if ((devlink = get_devlink(devlink_hdl, NULL, minor_path)) == NULL) {
651 			di_devfs_path_free (minor_path);
652         		continue;
653 		}
654 
655 		if (!rescan ||
656 		    (hal_device_store_match_key_value_string (hald_get_gdl (),
657 		    "solaris.devfs_path", minor_path) == NULL)) {
658 			devinfo_lofi_add_minor(d, node, minor_path, devlink, dev);
659 		}
660 
661 		di_devfs_path_free (minor_path);
662 		free(devlink);
663 	}
664 	di_devlink_fini (&devlink_hdl);
665 
666 	return (d);
667 }
668 
669 static void
670 devinfo_lofi_add_minor(HalDevice *parent, di_node_t node, char *minor_path, char *devlink, dev_t dev)
671 {
672 	HalDevice *d;
673 	char	*raw;
674 	char	*doslink;
675 	char	dospath[64];
676 	struct devinfo_storage_minor *m;
677 	int	i;
678 
679 	/* add storage */
680 	d = hal_device_new ();
681 
682 	devinfo_set_default_properties (d, parent, node, minor_path);
683         hal_device_property_set_string (d, "info.category", "storage");
684         hal_device_add_capability (d, "storage");
685        	hal_device_property_set_string (d, "storage.bus", "lofi");
686         hal_device_property_set_bool (d, "storage.hotpluggable", TRUE);
687         hal_device_property_set_bool (d, "storage.removable", FALSE);
688         hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
689        	hal_device_property_set_string (d, "storage.drive_type", "disk");
690         hal_device_add_capability (d, "block");
691 	hal_device_property_set_int (d, "block.major", major(dev));
692 	hal_device_property_set_int (d, "block.minor", minor(dev));
693 	hal_device_property_set_string (d, "block.device", devlink);
694 	raw = dsk_to_rdsk (devlink);
695 	hal_device_property_set_string (d, "block.solaris.raw_device", raw);
696 	free (raw);
697 	hal_device_property_set_bool (d, "block.is_volume", FALSE);
698 
699 	devinfo_add_enqueue (d, minor_path, &devinfo_storage_handler);
700 
701 	/* add volumes: one on main device and a few pcfs candidates */
702 	m = devinfo_storage_new_minor(minor_path, WHOLE_DISK, devlink, dev, -1);
703 	devinfo_volume_add (d, node, m);
704 	devinfo_storage_free_minor (m);
705 
706 	doslink = (char *)calloc (1, strlen (devlink) + sizeof (":NNN") + 1);
707 	if (doslink != NULL) {
708 		for (i = 1; i < 16; i++) {
709 			snprintf(dospath, sizeof (dospath), WHOLE_DISK":%d", i);
710 			sprintf(doslink, "%s:%d", devlink, i);
711 			m = devinfo_storage_new_minor(minor_path, dospath, doslink, dev, i);
712 			devinfo_volume_add (d, node, m);
713 			devinfo_storage_free_minor (m);
714 		}
715 		free (doslink);
716 	}
717 }
718 
719 void
720 devinfo_lofi_remove_minor(char *parent_devfs_path, char *name)
721 {
722 	GSList *i;
723 	GSList *devices;
724 	HalDevice *d = NULL;
725 	const char *devfs_path;
726 
727 	devices = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
728 		"block.solaris.raw_device", name);
729         for (i = devices; i != NULL; i = g_slist_next (i)) {
730 		if (hal_device_has_capability (HAL_DEVICE (i->data), "storage")) {
731 			d = HAL_DEVICE (i->data);
732 			break;
733 		}
734 	}
735 	g_slist_free (devices);
736 
737 	if (d == NULL) {
738 		HAL_INFO (("device not found %s", name));
739 		return;
740 	}
741 
742 	if ((devfs_path = hal_device_property_get_string (d,
743 	    "solaris.devfs_path")) == NULL) {
744 		HAL_INFO (("devfs_path not found %s", hal_device_get_udi (d)));
745 		return;
746 	}
747 
748 	if (d != NULL) {
749 		devinfo_remove_branch ((char *)devfs_path, d);
750 	}
751 }
752 
753 /* common storage */
754 
755 static void
756 devinfo_storage_free_minor(struct devinfo_storage_minor *m)
757 {
758 	if (m != NULL) {
759 		free (m->slice);
760 		free (m->devlink);
761 		free (m->devpath);
762 		free (m);
763 	}
764 }
765 
766 static struct devinfo_storage_minor *
767 devinfo_storage_new_minor(char *maindev_path, char *slice, char *devlink, dev_t dev, int dosnum)
768 {
769 	struct devinfo_storage_minor *m;
770 	int pathlen;
771 	char *devpath;
772 
773 	m = (struct devinfo_storage_minor *)calloc (sizeof (struct devinfo_storage_minor), 1);
774 	if (m != NULL) {
775 		/*
776 		 * For volume's devfs_path we'll use minor_path/slice instead of
777 		 * minor_path which we use for parent storage device.
778 		 */
779 		pathlen = strlen (maindev_path) + strlen (slice) + 2;
780 		devpath = (char *)calloc (1, pathlen);
781 		snprintf(devpath, pathlen, "%s/%s", maindev_path, slice);
782 
783 		m->devpath = devpath;
784 		m->devlink = strdup (devlink);
785 		m->slice = strdup (slice);
786 		m->dev = dev;
787 		m->dosnum = dosnum;
788 		if ((m->devpath == NULL) || (m->devlink == NULL)) {
789 			devinfo_storage_free_minor (m);
790 			m = NULL;
791 		}
792 	}
793 	return (m);
794 }
795 
796 /*
797  * Storage minor nodes are potential "volume" objects.
798  * This function also completes building the parent object (main storage device).
799  */
800 static void
801 devinfo_storage_minors(HalDevice *parent, di_node_t node, gchar *devfs_path, gboolean rescan)
802 {
803 	di_devlink_handle_t devlink_hdl;
804 	gboolean is_cdrom;
805 	const char *whole_disk;
806 	int     major;
807 	di_minor_t minor;
808 	dev_t   dev;
809 	char    *minor_path = NULL;
810 	char    *maindev_path = NULL;
811 	char    *devpath, *devlink;
812 	int	doslink_len;
813 	char	*doslink;
814 	char	dospath[64];
815 	char    *slice;
816 	int	pathlen;
817 	int	i;
818 	char	*raw;
819 	boolean_t maindev_is_d0;
820 	GQueue	*mq;
821 	HalDevice *volume;
822 	struct devinfo_storage_minor *m;
823 	struct devinfo_storage_minor *maindev = NULL;
824 
825 	/* for cdroms whole disk is always s2 */
826 	is_cdrom = hal_device_has_capability (parent, "storage.cdrom");
827 	whole_disk = is_cdrom ? "s2" : WHOLE_DISK;
828 
829 	major = di_driver_major(node);
830 
831 	/* the "whole disk" p0/s2/d0 node must come first in the hotplug queue
832 	 * so we put other minor nodes on the local queue and move to the
833 	 * hotplug queue up in the end
834 	 */
835 	if ((mq = g_queue_new()) == NULL) {
836 		goto err;
837 	}
838 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
839 		g_queue_free (mq);
840 		goto err;
841 	}
842 	minor = DI_MINOR_NIL;
843 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
844 		dev = di_minor_devt(minor);
845 		if ((major != major(dev)) ||
846 		    (di_minor_type(minor) != DDM_MINOR) ||
847 		    (di_minor_spectype(minor) != S_IFBLK) ||
848 		    ((minor_path = di_devfs_minor_path(minor)) == NULL)) {
849 			continue;
850 		}
851 		if ((devlink = get_devlink(devlink_hdl, NULL, minor_path)) == NULL) {
852 			di_devfs_path_free (minor_path);
853         		continue;
854 		}
855 
856 		slice = devinfo_volume_get_slice_name (devlink);
857 		if (strlen (slice) < 2) {
858 			free (devlink);
859 			di_devfs_path_free (minor_path);
860 			continue;
861 		}
862 
863 		/* ignore p1..N - we'll use p0:N instead */
864 		if ((strlen (slice) > 1) && (slice[0] == 'p') && isdigit(slice[1]) &&
865 		    ((atol(&slice[1])) > 0)) {
866 			free (devlink);
867 			di_devfs_path_free (minor_path);
868 			continue;
869 		}
870 
871 		m = devinfo_storage_new_minor(minor_path, slice, devlink, dev, -1);
872 		if (m == NULL) {
873 			free (devlink);
874 			di_devfs_path_free (minor_path);
875 			continue;
876 		}
877 
878 		/* main device is either s2/p0 or d0, the latter taking precedence */
879 		if ((strcmp (slice, "d0") == 0) ||
880 		    (((strcmp (slice, whole_disk) == 0) && (maindev == NULL)))) {
881 			if (maindev_path != NULL) {
882 				di_devfs_path_free (maindev_path);
883 			}
884 			maindev_path = minor_path;
885 			maindev = m;
886 			g_queue_push_head (mq, maindev);
887 		} else {
888 			di_devfs_path_free (minor_path);
889 			g_queue_push_tail (mq, m);
890 		}
891 
892 		free (devlink);
893 	}
894 	di_devlink_fini (&devlink_hdl);
895 
896 	if (maindev == NULL) {
897 		/* shouldn't typically happen */
898 		while (!g_queue_is_empty (mq)) {
899 			devinfo_storage_free_minor (g_queue_pop_head (mq));
900 		}
901 		goto err;
902 	}
903 
904 	/* first enqueue main storage device */
905 	if (!rescan) {
906 		hal_device_property_set_int (parent, "block.major", major);
907 		hal_device_property_set_int (parent, "block.minor", minor(maindev->dev));
908 		hal_device_property_set_string (parent, "block.device", maindev->devlink);
909 		raw = dsk_to_rdsk (maindev->devlink);
910 		hal_device_property_set_string (parent, "block.solaris.raw_device", raw);
911 		free (raw);
912 		hal_device_property_set_bool (parent, "block.is_volume", FALSE);
913 		hal_device_property_set_string (parent, "solaris.devfs_path", maindev_path);
914 		devinfo_add_enqueue (parent, maindev_path, &devinfo_storage_handler);
915 	}
916 
917 	/* add virtual dos volumes to enable pcfs probing */
918 	if (!is_cdrom) {
919 		doslink_len = strlen (maindev->devlink) + sizeof (":NNN") + 1;
920 		if ((doslink = (char *)calloc (1, doslink_len)) != NULL) {
921 			for (i = 1; i < 16; i++) {
922 				snprintf(dospath, sizeof (dospath), "%s:%d", maindev->slice, i);
923 				snprintf(doslink, doslink_len, "%s:%d", maindev->devlink, i);
924 				m = devinfo_storage_new_minor(maindev_path, dospath, doslink, maindev->dev, i);
925 				g_queue_push_tail (mq, m);
926 			}
927 			free (doslink);
928 		}
929 	}
930 
931 	maindev_is_d0 = (strcmp (maindev->slice, "d0") == 0);
932 
933 	/* enqueue all volumes */
934 	while (!g_queue_is_empty (mq)) {
935 		m = g_queue_pop_head (mq);
936 
937 		/* if main device is d0, we'll throw away s2/p0 */
938 		if (maindev_is_d0 && (strcmp (m->slice, whole_disk) == 0)) {
939 			devinfo_storage_free_minor (m);
940 			continue;
941 		}
942 		/* don't do p0 on cdrom */
943 		if (is_cdrom && (strcmp (m->slice, "p0") == 0)) {
944 			devinfo_storage_free_minor (m);
945 			continue;
946 		}
947 		if (rescan) {
948 			/* in rescan mode, don't reprobe existing volumes */
949 			/* XXX detect volume removal? */
950 			volume = hal_device_store_match_key_value_string (hald_get_gdl (),
951 			    "solaris.devfs_path", m->devpath);
952 			if ((volume == NULL) || !hal_device_has_capability(volume, "volume")) {
953 				devinfo_volume_add (parent, node, m);
954 			} else {
955 				HAL_INFO(("rescan volume exists %s", m->devpath));
956 			}
957 		} else {
958 			devinfo_volume_add (parent, node, m);
959 		}
960 		devinfo_storage_free_minor (m);
961 	}
962 
963 	if (maindev_path != NULL) {
964 		di_devfs_path_free (maindev_path);
965 	}
966 
967 	return;
968 
969 err:
970 	if (maindev_path != NULL) {
971 		di_devfs_path_free (maindev_path);
972 	}
973 	if (!rescan) {
974 		devinfo_add_enqueue (parent, devfs_path, &devinfo_storage_handler);
975 	}
976 }
977 
978 HalDevice *
979 devinfo_volume_add(HalDevice *parent, di_node_t node, devinfo_storage_minor_t *m)
980 {
981 	HalDevice *d;
982 	char	*raw;
983         char    udi[HAL_PATH_MAX];
984 	char	*devfs_path = m->devpath;
985 	char	*devlink = m->devlink;
986 	dev_t	dev = m->dev;
987 	int	dosnum = m->dosnum;
988 	char	*slice = m->slice;
989 
990 	HAL_INFO (("volume_add: devfs_path=%s devlink=%s", devfs_path, devlink));
991 	d = hal_device_new ();
992 
993 	devinfo_set_default_properties (d, parent, node, devfs_path);
994         hal_device_property_set_string (d, "info.category", "volume");
995 
996        	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
997 		"%s/%s", hal_device_get_udi (parent), slice);
998         hal_device_set_udi (d, udi);
999         hal_device_property_set_string (d, "info.udi", udi);
1000         hal_device_property_set_string (d, "info.product", slice);
1001 
1002        	hal_device_add_capability (d, "volume");
1003        	hal_device_add_capability (d, "block");
1004 	hal_device_property_set_int (d, "block.major", major (dev));
1005 	hal_device_property_set_int (d, "block.minor", minor (dev));
1006 	hal_device_property_set_string (d, "block.device", devlink);
1007 	raw = dsk_to_rdsk (devlink);
1008 	hal_device_property_set_string (d, "block.solaris.raw_device", raw);
1009 	free (raw);
1010 	hal_device_property_set_string (d, "block.solaris.slice", slice);
1011 	hal_device_property_set_bool (d, "block.is_volume", TRUE); /* XXX */
1012 
1013 	hal_device_property_set_string (d, "block.storage_device", hal_device_get_udi (parent));
1014 
1015 	/* set volume defaults */
1016 	hal_device_property_set_string (d, "volume.fstype", "");
1017 	hal_device_property_set_string (d, "volume.fsusage", "");
1018 	hal_device_property_set_string (d, "volume.fsversion", "");
1019 	hal_device_property_set_string (d, "volume.uuid", "");
1020 	hal_device_property_set_string (d, "volume.label", "");
1021 	hal_device_property_set_string (d, "volume.mount_point", "");
1022 	hal_device_property_set_bool (d, "volume.is_mounted", FALSE);
1023 	if (strcmp (hal_device_property_get_string (parent, "storage.drive_type"), "cdrom") == 0) {
1024 		hal_device_property_set_bool (d, "volume.is_disc", TRUE);
1025 		hal_device_add_capability (d, "volume.disc");
1026 	} else {
1027 		hal_device_property_set_bool (d, "volume.is_disc", FALSE);
1028 	}
1029 
1030 	if (dosnum > 0) {
1031 		hal_device_property_set_bool (d, "volume.is_partition", TRUE);
1032 		hal_device_property_set_int (d, "volume.partition.number", dosnum);
1033 	} else {
1034 		hal_device_property_set_bool (d, "volume.is_partition", FALSE);
1035 	}
1036 
1037 	/* prober may override these */
1038         hal_device_property_set_int (d, "volume.block_size", 512);
1039 
1040 	devinfo_add_enqueue (d, devfs_path, &devinfo_volume_handler);
1041 
1042 	return (d);
1043 }
1044 
1045 static void
1046 devinfo_volume_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
1047 {
1048 	void *end_token = (void *) userdata1;
1049 	char *whole_disk;
1050 	char *block_device;
1051 	const char *storage_udi;
1052 	HalDevice *storage_d;
1053 	const char *slice;
1054 	int dos_num;
1055 
1056 	if (hal_device_property_get_bool (d, "info.ignore")) {
1057 		HAL_INFO (("Preprobing merged info.ignore==TRUE %s", hal_device_get_udi (d)));
1058 		goto skip;
1059 	}
1060 
1061 	/*
1062 	 * Optimizations: only probe if there's a chance to find something
1063 	 */
1064 	block_device = (char *)hal_device_property_get_string (d, "block.device");
1065 	storage_udi = hal_device_property_get_string (d, "block.storage_device");
1066 	slice = hal_device_property_get_string(d, "block.solaris.slice");
1067 	if ((block_device == NULL) || (storage_udi == NULL) ||
1068 	    (slice == NULL) || (strlen (slice) < 2)) {
1069 		HAL_INFO (("Malformed volume properties %s", hal_device_get_udi (d)));
1070 		goto skip;
1071 	}
1072 	storage_d = hal_device_store_match_key_value_string (hald_get_gdl (), "info.udi", storage_udi);
1073 	if (storage_d == NULL) {
1074 		HAL_INFO (("Storage device not found %s", hal_device_get_udi (d)));
1075 		goto skip;
1076 	}
1077 
1078 	whole_disk = hal_device_has_capability (storage_d,
1079 	    "storage.cdrom") ? "s2" : WHOLE_DISK;
1080 
1081 	if (is_dos_path(block_device, &dos_num)) {
1082 		/* don't probe more dos volumes than probe-storage found */
1083 		if ((hal_device_property_get_bool (storage_d, "storage.no_partitions_hint") ||
1084 		    (dos_num > hal_device_property_get_int (storage_d, "storage.solaris.num_dos_partitions")))) {
1085 			    HAL_INFO (("%d > %d %s", dos_num, hal_device_property_get_int (storage_d,
1086 				"storage.solaris.num_dos_partitions"), hal_device_get_udi (storage_d)));
1087 			goto skip;
1088 		}
1089 	} else {
1090 		/* if no VTOC slices found, don't probe slices except s2 */
1091 		if ((slice[0] == 's') && (isdigit(slice[1])) && ((strcmp (slice, whole_disk)) != 0) &&
1092 		    !hal_device_property_get_bool (storage_d, "storage.solaris.vtoc_slices")) {
1093 			HAL_INFO (("Not probing slice %s", hal_device_get_udi (d)));
1094 			goto skip;
1095 		}
1096 	}
1097 
1098 	HAL_INFO(("Probing udi=%s", hal_device_get_udi (d)));
1099 	hald_runner_run (d,
1100 			"hald-probe-volume", NULL,
1101 			DEVINFO_PROBE_VOLUME_TIMEOUT,
1102 			devinfo_callouts_probing_done,
1103 			(gpointer) end_token, userdata2);
1104 
1105 	return;
1106 
1107 skip:
1108 	hal_device_store_remove (hald_get_tdl (), d);
1109 	g_object_unref (d);
1110 	hotplug_event_end (end_token);
1111 }
1112 
1113 static void
1114 devinfo_volume_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
1115 {
1116 	HAL_INFO(("Preprobing volume udi=%s", hal_device_get_udi (d)));
1117 
1118 	if (hal_device_property_get_bool (parent, "info.ignore")) {
1119 		HAL_INFO (("Ignoring volume: parent's info.ignore is TRUE"));
1120 		goto skip;
1121 	}
1122 
1123         /* add to TDL so preprobing callouts and prober can access it */
1124         hal_device_store_add (hald_get_tdl (), d);
1125 
1126         /* Process preprobe fdi files */
1127         di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
1128 
1129         /* Run preprobe callouts */
1130         hal_util_callout_device_preprobe (d, devinfo_volume_preprobing_done, end_token, handler);
1131 
1132 	return;
1133 
1134 skip:
1135 	g_object_unref (d);
1136 	hotplug_event_end (end_token);
1137 }
1138 
1139 void
1140 devinfo_storage_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
1141 {
1142 	const char *drive_type;
1143 	const char *p_udi;
1144 	HalDevice *p_d;
1145 	HalDevice *phys_d = NULL;
1146 	const char *phys_bus;
1147 	const char *bus;
1148 	static const char *busses[] = { "usb", "ide", "scsi", "ieee1394",
1149 					"pseudo" };
1150 	int i;
1151 
1152 	HAL_INFO (("Preprobing udi=%s", hal_device_get_udi (d)));
1153 
1154 	if (parent == NULL) {
1155 		HAL_INFO (("no parent %s", hal_device_get_udi (d)));
1156 		goto error;
1157 	}
1158 
1159 	/*
1160 	 * figure out physical device and bus, except for floppy
1161 	 */
1162 	drive_type = hal_device_property_get_string (d, "storage.drive_type");
1163 	if ((drive_type != NULL) && (strcmp (drive_type, "floppy") == 0)) {
1164 		goto skip_bus;
1165 	}
1166 
1167 	p_d = parent;
1168 	for (;;) {
1169 		bus = hal_device_property_get_string (p_d, "info.bus");
1170 		if (bus != NULL) {
1171 			for (i = 0; i < NELEM(busses); i++) {
1172 				if (strcmp(bus, busses[i]) == 0) {
1173 					phys_d = p_d;
1174 					phys_bus = busses[i];
1175 					break;
1176 				}
1177 			}
1178 		}
1179 		/* up the tree */
1180 		p_udi = hal_device_property_get_string (p_d, "info.parent");
1181 		if (p_udi == NULL) {
1182 			break;
1183 		}
1184 		p_d = hal_device_store_find (hald_get_gdl (), p_udi);
1185 	}
1186 	if (phys_d == NULL) {
1187 		HAL_INFO (("no physical device %s", hal_device_get_udi (d)));
1188 	} else {
1189 		hal_device_property_set_string (d, "storage.physical_device", hal_device_get_udi (phys_d));
1190 		hal_device_property_set_string (d, "storage.bus", phys_bus);
1191 	}
1192 
1193 skip_bus:
1194 
1195 	/* add to TDL so preprobing callouts and prober can access it */
1196 	hal_device_store_add (hald_get_tdl (), d);
1197 
1198 	/* Process preprobe fdi files */
1199 	di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
1200 
1201 	/* Run preprobe callouts */
1202 	hal_util_callout_device_preprobe (d, devinfo_callouts_preprobing_done, end_token, handler);
1203 
1204 	return;
1205 
1206 error:
1207 	g_object_unref (d);
1208 	hotplug_event_end (end_token);
1209 }
1210 
1211 static void
1212 devinfo_storage_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
1213 {
1214         void *end_token = (void *) userdata1;
1215 
1216 	HAL_INFO (("devinfo_storage_probing_done %s", hal_device_get_udi (d)));
1217 
1218         /* Discard device if probing reports failure */
1219         if (exit_type != HALD_RUN_SUCCESS || return_code != 0) {
1220 		HAL_INFO (("devinfo_storage_probing_done returning exit_type=%d return_code=%d", exit_type, return_code));
1221                 hal_device_store_remove (hald_get_tdl (), d);
1222                 g_object_unref (d);
1223                 hotplug_event_end (end_token);
1224 		return;
1225         }
1226 
1227 	devinfo_storage_set_nicknames (d);
1228 
1229         /* Merge properties from .fdi files */
1230         di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION);
1231         di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY);
1232 
1233 	hal_util_callout_device_add (d, devinfo_callouts_add_done, end_token, NULL);
1234 }
1235 
1236 const gchar *
1237 devinfo_storage_get_prober (HalDevice *d, int *timeout)
1238 {
1239 	*timeout = DEVINFO_PROBE_STORAGE_TIMEOUT;
1240 	return "hald-probe-storage";
1241 }
1242 
1243 const gchar *
1244 devinfo_volume_get_prober (HalDevice *d, int *timeout)
1245 {
1246 	*timeout = DEVINFO_PROBE_VOLUME_TIMEOUT;
1247 	return "hald-probe-volume";
1248 }
1249 
1250 /*
1251  * After reprobing storage, reprobe its volumes.
1252  */
1253 static void
1254 devinfo_storage_rescan_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
1255 {
1256         void *end_token = (void *) userdata1;
1257 	const char *devfs_path_orig = NULL;
1258 	char *devfs_path = NULL;
1259 	char *p;
1260 	di_node_t node;
1261 
1262 	HAL_INFO (("devinfo_storage_rescan_probing_done %s", hal_device_get_udi (d)));
1263 
1264 	devfs_path_orig = hal_device_property_get_string (d, "solaris.devfs_path");
1265 	if (devfs_path_orig == NULL) {
1266 		HAL_INFO (("device has no solaris.devfs_path"));
1267 		hotplug_event_process_queue ();
1268 		return;
1269 	}
1270 
1271 	/* strip trailing minor part if any */
1272 	if (strrchr(devfs_path_orig, ':') != NULL) {
1273 		if ((devfs_path = strdup (devfs_path_orig)) != NULL) {
1274 			p = strrchr(devfs_path, ':');
1275 			*p = '\0';
1276 		}
1277 	} else {
1278 		devfs_path = (char *)devfs_path_orig;
1279 	}
1280 
1281 	if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
1282 		HAL_INFO (("di_init %s failed %d %s", devfs_path, errno, hal_device_get_udi (d)));
1283 		hotplug_event_process_queue ();
1284 		return;
1285 	} else {
1286 		devinfo_storage_minors (d, node, (char *)devfs_path, TRUE);
1287 		di_fini (node);
1288 	}
1289 
1290 	if (devfs_path != devfs_path_orig) {
1291 		free (devfs_path);
1292 	}
1293 
1294 	hotplug_event_process_queue ();
1295 }
1296 
1297 /*
1298  * For removable media devices, check for "storage.removable.media_available".
1299  * For non-removable media devices, assume media is always there.
1300  *
1301  * If media is gone, enqueue remove events for all children volumes.
1302  * If media is there, first reprobe storage, then probe for new volumes (but leave existing volumes alone).
1303  */
1304 gboolean
1305 devinfo_storage_device_rescan (HalDevice *d)
1306 {
1307 	GSList *i;
1308 	GSList *volumes;
1309 	HalDevice *v;
1310 	gchar *v_devfs_path;
1311 	const char *drive_type;
1312 	gboolean is_floppy;
1313 	gboolean media_available;
1314 
1315 	HAL_INFO (("devinfo_storage_device_rescan udi=%s", hal_device_get_udi (d)));
1316 
1317 	if (hal_device_property_get_bool (d, "block.is_volume")) {
1318 		HAL_INFO (("nothing to do for volume"));
1319 		return (FALSE);
1320 	}
1321 
1322 	drive_type = hal_device_property_get_string (d, "storage.drive_type");
1323 	is_floppy = (drive_type != NULL) && (strcmp (drive_type, "floppy") == 0);
1324 
1325 	media_available = !hal_device_property_get_bool (d, "storage.removable") ||
1326 	    hal_device_property_get_bool (d, "storage.removable.media_available");
1327 
1328 	if (!media_available && !is_floppy) {
1329 		HAL_INFO (("media gone %s", hal_device_get_udi (d)));
1330 
1331 		volumes = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
1332         	    "block.storage_device", hal_device_get_udi (d));
1333 		for (i = volumes; i != NULL; i = g_slist_next (i)) {
1334         		v = HAL_DEVICE (i->data);
1335 			v_devfs_path = (gchar *)hal_device_property_get_string (v, "solaris.devfs_path");
1336 			HAL_INFO (("child volume %s", hal_device_get_udi (v)));
1337 			if ((v_devfs_path != NULL) && hal_device_has_capability (v, "volume")) {
1338 				HAL_INFO (("removing volume %s", hal_device_get_udi (v)));
1339 				devinfo_remove_enqueue (v_devfs_path, NULL);
1340 			} else {
1341 				HAL_INFO (("not a volume %s", hal_device_get_udi (v)));
1342 			}
1343 		}
1344 		g_slist_free (volumes);
1345 
1346 		hotplug_event_process_queue ();
1347 	} else if (is_floppy) {
1348 		HAL_INFO (("rescanning floppy %s", hal_device_get_udi (d)));
1349 
1350 		hald_runner_run (d,
1351 				 "hald-probe-storage --only-check-for-media", NULL,
1352 				 DEVINFO_PROBE_STORAGE_TIMEOUT,
1353 				 devinfo_floppy_rescan_probing_done,
1354 				 NULL, NULL);
1355 	} else {
1356 		HAL_INFO (("media available %s", hal_device_get_udi (d)));
1357 
1358 		hald_runner_run (d,
1359 				 "hald-probe-storage --only-check-for-media", NULL,
1360 				 DEVINFO_PROBE_STORAGE_TIMEOUT,
1361 				 devinfo_storage_rescan_probing_done,
1362 				 NULL, NULL);
1363 	}
1364 
1365 	return TRUE;
1366 }
1367 
1368 static char *
1369 devinfo_volume_get_slice_name (char *devlink)
1370 {
1371 	char	*part, *slice, *disk;
1372 	char	*s = NULL;
1373 	char	*p;
1374 
1375 	if ((p = strstr(devlink, "/lofi/")) != 0) {
1376 		return (p + sizeof ("/lofi/") - 1);
1377 	}
1378 
1379 	part = strrchr(devlink, 'p');
1380 	slice = strrchr(devlink, 's');
1381 	disk = strrchr(devlink, 'd');
1382 
1383 	if ((part != NULL) && (part > slice) && (part > disk)) {
1384 		s = part;
1385 	} else if ((slice != NULL) && (slice > disk)) {
1386 		s = slice;
1387 	} else {
1388 		s = disk;
1389 	}
1390 	if ((s != NULL) && isdigit(s[1])) {
1391 		return (s);
1392 	} else {
1393 		return ("");
1394 	}
1395 }
1396 
1397 static gboolean
1398 is_dos_path(char *path, int *partnum)
1399 {
1400 	char *p;
1401 
1402 	if ((p = strrchr (path, ':')) == NULL) {
1403 		return (FALSE);
1404 	}
1405 	return ((*partnum = atoi(p + 1)) != 0);
1406 }
1407 
1408 static gboolean
1409 dos_to_dev(char *path, char **devpath, int *partnum)
1410 {
1411 	char *p;
1412 
1413 	if ((p = strrchr (path, ':')) == NULL) {
1414 		return (FALSE);
1415 	}
1416 	if ((*partnum = atoi(p + 1)) == 0) {
1417 		return (FALSE);
1418 	}
1419 	p[0] = '\0';
1420 	*devpath = strdup(path);
1421 	p[0] = ':';
1422 	return (*devpath != NULL);
1423 }
1424 
1425 static void
1426 devinfo_storage_cleanup_mountpoint_cb (HalDevice *d, guint32 exit_type,
1427 		       gint return_code, gchar **error,
1428 		       gpointer data1, gpointer data2)
1429 {
1430 	char *mount_point = (char *) data1;
1431 
1432 	HAL_INFO (("Cleaned up mount point '%s'", mount_point));
1433 	g_free (mount_point);
1434 }
1435 
1436 
1437 void
1438 devinfo_storage_mnttab_event (HalDevice *hal_volume)
1439 {
1440 	FILE *fp = NULL;
1441         struct extmnttab m;
1442 	HalDevice *d;
1443 	unsigned int major;
1444 	unsigned int minor;
1445 	GSList *volumes = NULL;
1446 	GSList *v;
1447 	char *mount_point;
1448 	dbus_bool_t is_partition;
1449 	const char *fstype;
1450 	int partition_number;
1451 
1452 	if (hal_volume != NULL) {
1453 		volumes = g_slist_append (NULL, hal_volume);
1454 	} else {
1455 		volumes = hal_device_store_match_multiple_key_value_string (hald_get_gdl (), "info.category", "volume");
1456 	}
1457 	if (volumes == NULL) {
1458 		return;
1459 	}
1460 
1461 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
1462 		HAL_ERROR (("Open failed %s errno %d", MNTTAB, errno));
1463 		return;
1464 	}
1465 
1466 	while (getextmntent(fp, &m, 1) == 0) {
1467 		for (v = volumes; v != NULL; v = g_slist_next (v)) {
1468 			d = HAL_DEVICE (v->data);
1469 			major = hal_device_property_get_int (d, "block.major");
1470 			minor = hal_device_property_get_int (d, "block.minor");
1471 
1472 			/*
1473 			 * special handling for pcfs, which encodes logical
1474 			 * drive number into the 6 upper bits of the minor
1475 			 */
1476 			is_partition = hal_device_property_get_bool (d, "volume.is_partition");
1477 			partition_number = hal_device_property_get_int (d, "volume.partition.number");
1478 			fstype = hal_device_property_get_string (d, "volume.fstype");
1479 
1480 			if (is_partition && (partition_number > 0) && (strcmp (fstype, "pcfs") == 0)) {
1481 				minor |= partition_number << 12;
1482 			}
1483 
1484 			if (m.mnt_major != major || m.mnt_minor != minor) {
1485 				continue;
1486 			}
1487 
1488 			/* this volume matches the mnttab entry */
1489 			device_property_atomic_update_begin ();
1490 			hal_device_property_set_bool (d, "volume.is_mounted", TRUE);
1491 			hal_device_property_set_bool (d, "volume.is_mounted_read_only",
1492 						      hasmntopt ((struct mnttab *)&m, "ro") ? TRUE : FALSE);
1493 			hal_device_property_set_string (d, "volume.mount_point", m.mnt_mountp);
1494 			device_property_atomic_update_end ();
1495 
1496 			HAL_INFO (("set %s to be mounted at %s",
1497 				   hal_device_get_udi (d), m.mnt_mountp));
1498 			volumes = g_slist_delete_link (volumes, v);
1499 		}
1500 	}
1501 
1502 	/* all remaining volumes are not mounted */
1503 	for (v = volumes; v != NULL; v = g_slist_next (v)) {
1504 		d = HAL_DEVICE (v->data);
1505 		mount_point = g_strdup (hal_device_property_get_string (d, "volume.mount_point"));
1506 		if (mount_point == NULL || strlen (mount_point) == 0) {
1507 			g_free (mount_point);
1508 			continue;
1509 		}
1510 
1511 		device_property_atomic_update_begin ();
1512 		hal_device_property_set_bool (d, "volume.is_mounted", FALSE);
1513 		hal_device_property_set_bool (d, "volume.is_mounted_read_only", FALSE);
1514 		hal_device_property_set_string (d, "volume.mount_point", "");
1515 		device_property_atomic_update_end ();
1516 
1517 		HAL_INFO (("set %s to unmounted", hal_device_get_udi (d)));
1518 
1519 		/* cleanup if was mounted by us */
1520 		if (hal_util_is_mounted_by_hald (mount_point)) {
1521 			char *cleanup_stdin;
1522 			char *extra_env[2];
1523 
1524 			HAL_INFO (("Cleaning up '%s'", mount_point));
1525 
1526 			extra_env[0] = g_strdup_printf ("HALD_CLEANUP=%s", mount_point);
1527 			extra_env[1] = NULL;
1528 			cleanup_stdin = "\n";
1529 
1530 			hald_runner_run_method (d,
1531 						"hal-storage-cleanup-mountpoint",
1532 						extra_env,
1533 						cleanup_stdin, TRUE,
1534 						0,
1535 						devinfo_storage_cleanup_mountpoint_cb,
1536 						g_strdup (mount_point), NULL);
1537 
1538 			g_free (extra_env[0]);
1539 		}
1540 
1541 		g_free (mount_point);
1542 	}
1543 	g_slist_free (volumes);
1544 
1545 	(void) fclose (fp);
1546 }
1547 
1548 static void
1549 devinfo_volume_force_unmount_cb (HalDevice *d, guint32 exit_type,
1550 		  gint return_code, gchar **error,
1551 		  gpointer data1, gpointer data2)
1552 {
1553 	void *end_token = (void *) data1;
1554 
1555 	HAL_INFO (("devinfo_volume_force_unmount_cb for udi='%s', exit_type=%d, return_code=%d", hal_device_get_udi (d), exit_type, return_code));
1556 
1557 	if (exit_type == HALD_RUN_SUCCESS && error != NULL &&
1558 	    error[0] != NULL && error[1] != NULL) {
1559 		char *exp_name = NULL;
1560 		char *exp_detail = NULL;
1561 
1562 		exp_name = error[0];
1563 		if (error[0] != NULL) {
1564 			exp_detail = error[1];
1565 		}
1566 		HAL_INFO (("failed with '%s' '%s'", exp_name, exp_detail));
1567 	}
1568 
1569 	hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
1570 }
1571 
1572 static void
1573 devinfo_volume_force_unmount (HalDevice *d, void *end_token)
1574 {
1575 	const char *device_file;
1576 	const char *mount_point;
1577 	char *unmount_stdin;
1578 	char *extra_env[2];
1579 	extra_env[0] = "HAL_METHOD_INVOKED_BY_UID=0";
1580 	extra_env[1] = NULL;
1581 
1582 	device_file = hal_device_property_get_string (d, "block.device");
1583 	mount_point = hal_device_property_get_string (d, "volume.mount_point");
1584 
1585 	if (mount_point == NULL || strlen (mount_point) == 0 || !hal_util_is_mounted_by_hald (mount_point)) {
1586 		hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
1587 		return;
1588 	}
1589 
1590 	HAL_INFO (("devinfo_volume_force_unmount for udi='%s'", hal_device_get_udi (d)));
1591 
1592 	unmount_stdin = "\n";
1593 
1594 	hald_runner_run_method (d,
1595 				"hal-storage-unmount",
1596 				extra_env,
1597 				unmount_stdin, TRUE,
1598 				0,
1599 				devinfo_volume_force_unmount_cb,
1600 				end_token, NULL);
1601 }
1602 
1603 void
1604 devinfo_volume_hotplug_begin_remove (HalDevice *d, char *devfs_path, void *end_token)
1605 {
1606 	if (hal_device_property_get_bool (d, "volume.is_mounted")) {
1607 		devinfo_volume_force_unmount (d, end_token);
1608 	} else {
1609 		hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
1610 	}
1611 }
1612 
1613 
1614 enum {
1615 	LEGACY_CDROM,
1616 	LEGACY_FLOPPY,
1617 	LEGACY_RMDISK
1618 };
1619 
1620 static const char *legacy_media_str[] = {
1621 	"cdrom",
1622 	"floppy",
1623 	"rmdisk"
1624 };
1625 
1626 struct enum_nick {
1627 	const char *type;
1628 	GSList	*nums;
1629 };
1630 
1631 static int
1632 devinfo_storage_get_legacy_media(HalDevice *d)
1633 {
1634 	const char *drive_type;
1635 
1636 	if (hal_device_has_capability (d, "storage.cdrom")) {
1637 		return (LEGACY_CDROM);
1638 	} else if (((drive_type = hal_device_property_get_string (d,
1639 	    "storage.drive_type")) != NULL) && (strcmp (drive_type, "floppy") == 0)) {
1640 		return (LEGACY_FLOPPY);
1641 	} else if (hal_device_property_get_bool (d, "storage.removable") ||
1642 	           hal_device_property_get_bool (d, "storage.hotpluggable")) {
1643 		return (LEGACY_RMDISK);
1644 	} else {
1645 		return (-1);
1646 	}
1647 }
1648 
1649 static gboolean
1650 devinfo_storage_foreach_nick (HalDeviceStore *store, HalDevice *d, gpointer user_data)
1651 {
1652 	struct enum_nick *en = (struct enum_nick *) user_data;
1653 	const char *media_type;
1654 	int media_num;
1655 
1656 	media_type = hal_device_property_get_string (d, "storage.solaris.legacy.media_type");
1657 	media_num = hal_device_property_get_int (d, "storage.solaris.legacy.media_num");
1658 	if ((media_type != NULL) && (strcmp (media_type, en->type) == 0) &&
1659 	    (media_num >= 0)) {
1660 		en->nums = g_slist_prepend (en->nums, GINT_TO_POINTER(media_num));
1661 	}
1662 	return TRUE;
1663 }
1664 
1665 static void
1666 devinfo_storage_append_nickname (HalDevice *d, const char *media_type, int media_num)
1667 {
1668 	char buf[64];
1669 
1670 	if (media_num == 0) {
1671 		hal_device_property_strlist_append (d, "storage.solaris.nicknames", media_type);
1672 	}
1673 	snprintf(buf, sizeof (buf), "%s%d", media_type, media_num);
1674 	hal_device_property_strlist_append (d, "storage.solaris.nicknames", buf);
1675 }
1676 
1677 static void
1678 devinfo_storage_set_nicknames (HalDevice *d)
1679 {
1680 	int media;
1681 	const char *media_type;
1682 	int media_num;
1683 	GSList *i;
1684 	struct enum_nick en;
1685 	char buf[64];
1686 
1687 	if ((media = devinfo_storage_get_legacy_media (d)) < 0) {
1688 		return;
1689 	}
1690 	media_type = legacy_media_str[media];
1691 
1692 	/* enumerate all storage devices of this media type */
1693 	en.type = media_type;
1694 	en.nums = NULL;
1695 	hal_device_store_foreach (hald_get_gdl (), devinfo_storage_foreach_nick, &en);
1696 
1697 	/* find a free number */
1698 	for (media_num = 0; ; media_num++) {
1699 		for (i = en.nums; i != NULL; i = g_slist_next (i)) {
1700         		if (GPOINTER_TO_INT (i->data) == media_num) {
1701 				break;
1702 			}
1703 		}
1704 		if (i == NULL) {
1705 			break;
1706 		}
1707 	}
1708 	g_slist_free (en.nums);
1709 
1710 	hal_device_property_set_string (d, "storage.solaris.legacy.media_type", media_type);
1711 	hal_device_property_set_int (d, "storage.solaris.legacy.media_num", media_num);
1712 
1713 	/* primary nickname, and also vold-style symdev */
1714 	snprintf(buf, sizeof (buf), "%s%d", media_type, media_num);
1715 	hal_device_property_set_string (d, "storage.solaris.legacy.symdev", buf);
1716 	devinfo_storage_append_nickname(d, media_type, media_num);
1717 
1718 	/* additional nicknames */
1719 	if (media == LEGACY_CDROM) {
1720 		devinfo_storage_append_nickname(d, "cd", media_num);
1721 		devinfo_storage_append_nickname(d, "sr", media_num);
1722 	} else if (media == LEGACY_FLOPPY) {
1723 		devinfo_storage_append_nickname(d, "fd", media_num);
1724 		devinfo_storage_append_nickname(d, "diskette", media_num);
1725 		devinfo_storage_append_nickname(d, "rdiskette", media_num);
1726 	}
1727 }
1728