devinfo_storage.c revision 6112:28ee9a48b832
1/***************************************************************************
2 *
3 * devinfo_storage.c : storage devices
4 *
5 * Copyright 2008 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
52typedef 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
60HalDevice *devinfo_ide_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
61static HalDevice *devinfo_ide_host_add(HalDevice *parent, di_node_t node, char *devfs_path);
62static HalDevice *devinfo_ide_device_add(HalDevice *parent, di_node_t node, char *devfs_path);
63static HalDevice *devinfo_ide_storage_add(HalDevice *parent, di_node_t node, char *devfs_path);
64HalDevice *devinfo_scsi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
65static HalDevice *devinfo_scsi_storage_add(HalDevice *parent, di_node_t node, char *devfs_path);
66HalDevice *devinfo_pcata_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
67static HalDevice *devinfo_pcata_storage_add(HalDevice *parent, di_node_t node, char *devfs_path);
68HalDevice *devinfo_floppy_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
69static void devinfo_floppy_add_volume(HalDevice *parent, di_node_t node);
70static HalDevice *devinfo_lofi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
71static void devinfo_lofi_add_minor(HalDevice *parent, di_node_t node, char *minor_path, char *devlink, dev_t dev);
72static void devinfo_storage_minors(HalDevice *parent, di_node_t node, gchar *devfs_path, gboolean);
73static struct devinfo_storage_minor *devinfo_storage_new_minor(char *maindev_path, char *slice,
74    char *devlink, dev_t dev, int dosnum);
75static void devinfo_storage_free_minor(struct devinfo_storage_minor *m);
76HalDevice *devinfo_volume_add(HalDevice *parent, di_node_t node, devinfo_storage_minor_t *m);
77static void devinfo_volume_preprobing_done(HalDevice *d, gpointer userdata1, gpointer userdata2);
78static void devinfo_volume_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token);
79static void devinfo_storage_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token);
80static void devinfo_storage_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2);
81const gchar *devinfo_volume_get_prober (HalDevice *d, int *timeout);
82const gchar *devinfo_storage_get_prober (HalDevice *d, int *timeout);
83
84static char *devinfo_scsi_dtype2str(int dtype);
85static char *devinfo_volume_get_slice_name (char *devlink);
86static gboolean dos_to_dev(char *path, char **devpath, int *partnum);
87static gboolean is_dos_path(char *path, int *partnum);
88
89static void devinfo_storage_set_nicknames (HalDevice *d);
90
91DevinfoDevHandler devinfo_ide_handler = {
92        devinfo_ide_add,
93	NULL,
94	NULL,
95	NULL,
96	NULL,
97        NULL
98};
99DevinfoDevHandler devinfo_scsi_handler = {
100        devinfo_scsi_add,
101	NULL,
102	NULL,
103	NULL,
104	NULL,
105        NULL
106};
107DevinfoDevHandler devinfo_pcata_handler = {
108        devinfo_pcata_add,
109	NULL,
110	NULL,
111	NULL,
112	NULL,
113        NULL
114};
115DevinfoDevHandler devinfo_floppy_handler = {
116        devinfo_floppy_add,
117	NULL,
118	NULL,
119	NULL,
120	NULL,
121        NULL
122};
123DevinfoDevHandler devinfo_lofi_handler = {
124        devinfo_lofi_add,
125	NULL,
126	NULL,
127	NULL,
128	NULL,
129        NULL
130};
131DevinfoDevHandler 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};
139DevinfoDevHandler 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
150HalDevice *
151devinfo_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
167static HalDevice *
168devinfo_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.subsystem", "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
184static HalDevice *
185devinfo_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.subsystem", "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
202static HalDevice *
203devinfo_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
248HalDevice *
249devinfo_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.subsystem", "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
283static HalDevice *
284devinfo_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
342static char *
343devinfo_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
373HalDevice *
374devinfo_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.subsystem", "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
402static HalDevice *
403devinfo_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
440HalDevice *
441devinfo_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
516out:
517	di_devfs_path_free (minor_path);
518	free(devlink);
519
520	return (d);
521}
522
523static void
524devinfo_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 */
546static void
547devinfo_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
589HalDevice *
590devinfo_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
595HalDevice *
596devinfo_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.subsystem", "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
669static void
670devinfo_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
719void
720devinfo_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
755static void
756devinfo_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
766static struct devinfo_storage_minor *
767devinfo_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 */
800static void
801devinfo_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
969err:
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
978HalDevice *
979devinfo_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
1045static void
1046devinfo_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
1107skip:
1108	hal_device_store_remove (hald_get_tdl (), d);
1109	g_object_unref (d);
1110	hotplug_event_end (end_token);
1111}
1112
1113static void
1114devinfo_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
1134skip:
1135	g_object_unref (d);
1136	hotplug_event_end (end_token);
1137}
1138
1139void
1140devinfo_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.subsystem");
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
1193skip_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
1206error:
1207	g_object_unref (d);
1208	hotplug_event_end (end_token);
1209}
1210
1211static void
1212devinfo_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
1236const gchar *
1237devinfo_storage_get_prober (HalDevice *d, int *timeout)
1238{
1239	*timeout = DEVINFO_PROBE_STORAGE_TIMEOUT;
1240	return "hald-probe-storage";
1241}
1242
1243const gchar *
1244devinfo_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 */
1253static void
1254devinfo_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 */
1304gboolean
1305devinfo_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
1368static char *
1369devinfo_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
1397static gboolean
1398is_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
1408static gboolean
1409dos_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
1425static void
1426devinfo_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
1437void
1438devinfo_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
1548static void
1549devinfo_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
1572static void
1573devinfo_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
1603void
1604devinfo_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
1614enum {
1615	LEGACY_CDROM,
1616	LEGACY_FLOPPY,
1617	LEGACY_RMDISK
1618};
1619
1620static const char *legacy_media_str[] = {
1621	"cdrom",
1622	"floppy",
1623	"rmdisk"
1624};
1625
1626struct enum_nick {
1627	const char *type;
1628	GSList	*nums;
1629};
1630
1631static int
1632devinfo_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
1649static gboolean
1650devinfo_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
1665static void
1666devinfo_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
1677static void
1678devinfo_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