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