1 /***************************************************************************
3 * addon-storage.c : watch removable media state changes
5 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
6 * Use is subject to license terms.
8 * Licensed under the Academic Free License version 2.1
10 **************************************************************************/
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
24 #include <sys/types.h>
28 #include <sys/mnttab.h>
31 #include <libsysevent.h>
32 #include <sys/sysevent/dev.h>
36 #include "../../hald/logger.h"
38 #define SLEEP_PERIOD 5
41 static char *devfs_path
;
42 LibHalContext
*ctx
= NULL
;
43 static sysevent_handle_t
*shp
= NULL
;
45 static void sysevent_dev_handler(sysevent_t
*);
48 my_dbus_error_free(DBusError
*error
)
50 if (dbus_error_is_set(error
)) {
51 dbus_error_free(error
);
60 shp
= sysevent_bind_handle (sysevent_dev_handler
);
62 HAL_DEBUG (("sysevent_bind_handle failed %d", errno
));
66 subcl
[0] = ESC_DEV_EJECT_REQUEST
;
67 if (sysevent_subscribe_event (shp
, EC_DEV_STATUS
, subcl
, 1) != 0) {
68 HAL_INFO (("subscribe(dev_status) failed %d", errno
));
69 sysevent_unbind_handle (shp
);
78 sysevent_unbind_handle (shp
);
84 sysevent_dev_handler (sysevent_t
*ev
)
89 char *phys_path
, *path
;
94 if ((class = sysevent_get_class_name (ev
)) == NULL
)
97 if ((subclass
= sysevent_get_subclass_name (ev
)) == NULL
)
100 if ((strcmp (class, EC_DEV_STATUS
) != 0) ||
101 (strcmp (subclass
, ESC_DEV_EJECT_REQUEST
) != 0))
104 if (sysevent_get_attr_list (ev
, &attr_list
) != 0)
107 if (nvlist_lookup_string (attr_list
, DEV_PHYS_PATH
, &phys_path
) != 0) {
111 /* see if event belongs to our LUN (ignore slice and "/devices" ) */
112 if (strncmp (phys_path
, "/devices", sizeof ("/devices") - 1) == 0)
113 path
= phys_path
+ sizeof ("/devices") - 1;
117 if ((p
= strrchr (path
, ':')) == NULL
)
119 len
= (uintptr_t)p
- (uintptr_t)path
;
120 if (strncmp (path
, devfs_path
, len
) != 0)
123 HAL_DEBUG (("sysevent_dev_handler %s %s", subclass
, phys_path
));
125 /* we got it, tell the world */
126 dbus_error_init (&error
);
127 libhal_device_emit_condition (ctx
, udi
, "EjectPressed", "", &error
);
128 dbus_error_free (&error
);
131 nvlist_free(attr_list
);
135 force_unmount (LibHalContext
*ctx
, const char *udi
)
138 DBusMessage
*msg
= NULL
;
139 DBusMessage
*reply
= NULL
;
140 char **options
= NULL
;
141 unsigned int num_options
= 0;
142 DBusConnection
*dbus_connection
;
145 dbus_error_init (&error
);
147 dbus_connection
= libhal_ctx_get_dbus_connection (ctx
);
149 msg
= dbus_message_new_method_call ("org.freedesktop.Hal", udi
,
150 "org.freedesktop.Hal.Device.Volume",
153 HAL_DEBUG (("Could not create dbus message for %s", udi
));
158 options
= calloc (1, sizeof (char *));
159 if (options
== NULL
) {
160 HAL_DEBUG (("Could not allocate options array"));
164 device_file
= libhal_device_get_property_string (ctx
, udi
, "block.device", &error
);
165 if (device_file
!= NULL
) {
166 libhal_free_string (device_file
);
168 dbus_error_free (&error
);
170 if (!dbus_message_append_args (msg
,
171 DBUS_TYPE_ARRAY
, DBUS_TYPE_STRING
, &options
, num_options
,
172 DBUS_TYPE_INVALID
)) {
173 HAL_DEBUG (("Could not append args to dbus message for %s", udi
));
177 if (!(reply
= dbus_connection_send_with_reply_and_block (dbus_connection
, msg
, -1, &error
))) {
178 HAL_DEBUG (("Unmount failed for %s: %s : %s\n", udi
, error
.name
, error
.message
));
182 if (dbus_error_is_set (&error
)) {
183 HAL_DEBUG (("Unmount failed for %s\n%s : %s\n", udi
, error
.name
, error
.message
));
187 HAL_DEBUG (("Succesfully unmounted udi '%s'", udi
));
190 dbus_error_free (&error
);
193 dbus_message_unref (msg
);
195 dbus_message_unref (reply
);
199 unmount_childs (LibHalContext
*ctx
, const char *udi
)
205 dbus_error_init (&error
);
207 /* need to force unmount all partitions */
208 if ((volumes
= libhal_manager_find_device_string_match (
209 ctx
, "block.storage_device", udi
, &num_volumes
, &error
)) != NULL
) {
210 dbus_error_free (&error
);
213 for (i
= 0; i
< num_volumes
; i
++) {
216 vol_udi
= volumes
[i
];
217 if (libhal_device_get_property_bool (ctx
, vol_udi
, "block.is_volume", &error
)) {
218 dbus_error_free (&error
);
219 if (libhal_device_get_property_bool (ctx
, vol_udi
, "volume.is_mounted", &error
)) {
220 dbus_error_free (&error
);
221 HAL_DEBUG (("Forcing unmount of child '%s'", vol_udi
));
222 force_unmount (ctx
, vol_udi
);
226 libhal_free_string_array (volumes
);
228 my_dbus_error_free (&error
);
231 /** Check if a filesystem on a special device file is mounted
233 * @param device_file Special device file, e.g. /dev/cdrom
234 * @return TRUE iff there is a filesystem system mounted
235 * on the special device file
238 is_mounted (const char *device_file
)
241 dbus_bool_t rc
= FALSE
;
245 if ((f
= fopen ("/etc/mnttab", "r")) == NULL
)
248 bzero(&mp
, sizeof (mp
));
249 bzero(&mpref
, sizeof (mpref
));
250 mpref
.mnt_special
= (char *)device_file
;
251 if (getmntany(f
, &mp
, &mpref
) == 0) {
260 close_device (int *fd
)
271 priv_set_t
*pPrivSet
= NULL
;
272 priv_set_t
*lPrivSet
= NULL
;
275 * Start with the 'basic' privilege set and then remove any
276 * of the 'basic' privileges that will not be needed.
278 if ((pPrivSet
= priv_str_to_set("basic", ",", NULL
)) == NULL
) {
282 /* Clear privileges we will not need from the 'basic' set */
283 (void) priv_delset(pPrivSet
, PRIV_FILE_LINK_ANY
);
284 (void) priv_delset(pPrivSet
, PRIV_PROC_INFO
);
285 (void) priv_delset(pPrivSet
, PRIV_PROC_SESSION
);
287 /* to open logindevperm'd devices */
288 (void) priv_addset(pPrivSet
, PRIV_FILE_DAC_READ
);
290 /* to receive sysevents */
291 (void) priv_addset(pPrivSet
, PRIV_SYS_CONFIG
);
293 /* Set the permitted privilege set. */
294 if (setppriv(PRIV_SET
, PRIV_PERMITTED
, pPrivSet
) != 0) {
298 /* Clear the limit set. */
299 if ((lPrivSet
= priv_allocset()) == NULL
) {
303 priv_emptyset(lPrivSet
);
305 if (setppriv(PRIV_SET
, PRIV_LIMIT
, lPrivSet
) != 0) {
309 priv_freeset(lPrivSet
);
313 main (int argc
, char *argv
[])
315 char *device_file
, *raw_device_file
;
319 int state
, last_state
;
320 char *support_media_changed_str
;
321 int support_media_changed
;
324 if ((udi
= getenv ("UDI")) == NULL
)
326 if ((device_file
= getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL
)
328 if ((raw_device_file
= getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL
)
330 if ((bus
= getenv ("HAL_PROP_STORAGE_BUS")) == NULL
)
332 if ((drive_type
= getenv ("HAL_PROP_STORAGE_DRIVE_TYPE")) == NULL
)
334 if ((devfs_path
= getenv ("HAL_PROP_SOLARIS_DEVFS_PATH")) == NULL
)
343 support_media_changed_str
= getenv ("HAL_PROP_STORAGE_CDROM_SUPPORT_MEDIA_CHANGED");
344 if (support_media_changed_str
!= NULL
&& strcmp (support_media_changed_str
, "true") == 0)
345 support_media_changed
= TRUE
;
347 support_media_changed
= FALSE
;
349 dbus_error_init (&error
);
351 if ((ctx
= libhal_ctx_init_direct (&error
)) == NULL
) {
354 my_dbus_error_free (&error
);
356 if (!libhal_device_addon_is_ready (ctx
, udi
, &error
)) {
359 my_dbus_error_free (&error
);
361 printf ("Doing addon-storage for %s (bus %s) (drive_type %s) (udi %s)\n", device_file
, bus
, drive_type
, udi
);
363 last_state
= state
= DKIO_NONE
;
365 /* Linux version of this addon attempts to re-open the device O_EXCL
366 * every 2 seconds, trying to figure out if some other app,
367 * like a cd burner, is using the device. Aside from questionable
368 * value of this (apps should use HAL's locked property or/and
369 * Solaris in_use facility), but also frequent opens/closes
370 * keeps media constantly spun up. All this needs more thought.
373 if (is_mounted (device_file
)) {
375 sleep (SLEEP_PERIOD
);
376 } else if ((fd
< 0) && ((fd
= open (raw_device_file
, O_RDONLY
| O_NONBLOCK
)) < 0)) {
377 HAL_DEBUG (("open failed for %s: %s", raw_device_file
, strerror (errno
)));
378 sleep (SLEEP_PERIOD
);
380 /* Check if a disc is in the drive */
381 /* XXX initial call always returns inserted
382 * causing unnecessary rescan - optimize?
384 if (ioctl (fd
, DKIOCSTATE
, &state
) == 0) {
385 if (state
== last_state
) {
386 HAL_DEBUG (("state has not changed %d %s", state
, device_file
));
389 HAL_DEBUG (("new state %d %s", state
, device_file
));
394 HAL_DEBUG (("Media removal detected on %s", device_file
));
397 libhal_device_set_property_bool (ctx
, udi
, "storage.removable.media_available", FALSE
, &error
);
398 my_dbus_error_free (&error
);
400 /* attempt to unmount all childs */
401 unmount_childs (ctx
, udi
);
403 /* could have a fs on the main block device; do a rescan to remove it */
404 libhal_device_rescan (ctx
, udi
, &error
);
405 my_dbus_error_free (&error
);
409 HAL_DEBUG (("Media insertion detected on %s", device_file
));
412 libhal_device_set_property_bool (ctx
, udi
, "storage.removable.media_available", TRUE
, &error
);
413 my_dbus_error_free (&error
);
415 /* could have a fs on the main block device; do a rescan to add it */
416 libhal_device_rescan (ctx
, udi
, &error
);
417 my_dbus_error_free (&error
);
421 HAL_DEBUG (("Device gone detected on %s", device_file
));
424 unmount_childs (ctx
, udi
);
433 HAL_DEBUG (("DKIOCSTATE failed: %s\n", strerror(errno
)));
434 sleep (SLEEP_PERIOD
);
442 my_dbus_error_free (&error
);
443 libhal_ctx_shutdown (ctx
, &error
);
444 libhal_ctx_free (ctx
);